From eaa9eb3bd689122343bd10c3cecc2286955f41ac Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 1 Sep 2023 14:46:51 +0900 Subject: [PATCH 001/191] ALlow Chevalley-Eilenberg complexes to have coefficients over general finite dimensional modules. --- ...ite_dimensional_lie_algebras_with_basis.py | 207 +++++++++++++----- 1 file changed, 153 insertions(+), 54 deletions(-) diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 61510508be9..8ae899bf6b7 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -27,6 +27,31 @@ from sage.sets.family import Family +def _ce_complex_key(self, M, d, s, n): + """ + The key for caching the Chevalley-Eilenberg complex. + + TESTS:: + + sage: from sage.categories.finite_dimensional_lie_algebras_with_basis import _ce_complex_key + sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}}) + sage: _ce_complex_key(L, None, False, True, 5) + (None, False, True) + sage: f = ({x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}) + sage: _ce_complex_key(L, f, False, True, 5) + (Representation of Lie algebra on 2 generators (x, y) over Rational Field defined by: + [1 0] + x |--> [0 0] + [0 1] + y |--> [0 0], + False, + True) + """ + if isinstance(M, dict): + M = self.representation(M) + return (M, d, s) + + class FiniteDimensionalLieAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ Category of finite dimensional Lie algebras with a basis. @@ -1103,7 +1128,7 @@ def is_semisimple(self): """ return not self.killing_form_matrix().is_singular() - @cached_method(key=lambda self,M,d,s,n: (M,d,s)) + @cached_method(key=_ce_complex_key) def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=None): r""" Return the Chevalley-Eilenberg complex of ``self``. @@ -1134,7 +1159,13 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non INPUT: - ``M`` -- (default: the trivial 1-dimensional module) - the module `M` + one of the following: + + * a module `M` with an action of ``self`` + * a dictionary whose keys are basis elements and values + are matrices representing a Lie algebra homomorphism + defining the representation + - ``dual`` -- (default: ``False``) if ``True``, causes the dual of the complex to be computed - ``sparse`` -- (default: ``True``) whether to use sparse @@ -1148,15 +1179,15 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sage: C = L.chevalley_eilenberg_complex(); C Chain complex with at most 4 nonzero terms over Integer Ring sage: ascii_art(C) - [ 2 0 0] [0] - [ 0 -1 0] [0] - [0 0 0] [ 0 0 2] [0] + [-2 0 0] [0] + [ 0 1 0] [0] + [0 0 0] [ 0 0 -2] [0] 0 <-- C_0 <-------- C_1 <----------- C_2 <---- C_3 <-- 0 - sage: # long time, needs sage.combinat sage.modules + sage: # needs sage.combinat sage.modules sage: L = LieAlgebra(QQ, cartan_type=['C',2]) - sage: C = L.chevalley_eilenberg_complex() - sage: [C.free_module_rank(i) for i in range(11)] + sage: C = L.chevalley_eilenberg_complex() # long time + sage: [C.free_module_rank(i) for i in range(11)] # long time [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1] sage: # needs sage.combinat sage.modules @@ -1164,47 +1195,63 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sage: E, F, H = g.basis() sage: n = g.subalgebra([F, H]) sage: ascii_art(n.chevalley_eilenberg_complex()) - [0] - [0 0] [2] + [ 0] + [0 0] [-2] 0 <-- C_0 <------ C_1 <---- C_2 <-- 0 + sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}}) + sage: f = ({x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}) + sage: C = L.chevalley_eilenberg_complex(f); C + Chain complex with at most 3 nonzero terms over Rational Field + sage: ascii_art(C) + [ 0 -1] + [ 2 0] + [1 0 0 1] [ 0 0] + [0 0 0 0] [ 0 1] + 0 <-- C_0 <---------- C_1 <-------- C_2 <-- 0 + + sage: ascii_art(L.chevalley_eilenberg_complex(f, sparse=False)) + [ 0 -1] + [ 2 0] + [1 0 0 1] [ 0 0] + [0 0 0 0] [ 0 1] + 0 <-- C_0 <---------- C_1 <-------- C_2 <-- 0 + REFERENCES: - :wikipedia:`Lie_algebra_cohomology#Chevalley-Eilenberg_complex` - [Wei1994]_ Chapter 7 - - .. TODO:: - - Currently this is only implemented for coefficients - given by the trivial module `R`, where `R` is the - base ring and `g R = 0` for all `g \in \mathfrak{g}`. - Allow generic coefficient modules `M`. """ if dual: return self.chevalley_eilenberg_complex(M, dual=False, sparse=sparse, ncpus=ncpus).dual() - if M is not None: - raise NotImplementedError("only implemented for the default" - " (the trivial module)") - - from itertools import combinations + import itertools + from itertools import combinations, product from sage.arith.misc import binomial from sage.matrix.matrix_space import MatrixSpace + from sage.algebras.lie_algebras.representation import Representation_abstract R = self.base_ring() zero = R.zero() mone = -R.one() - if M is not None: - raise NotImplementedError("coefficient module M cannot be passed") # Make sure we specify the ordering of the basis - B = self.basis() - K = list(B.keys()) - B = [B[k] for k in K] - Ind = list(range(len(K))) - M = self.module() - ambient = M.is_ambient() + LB = self.basis() + LK = list(LB.keys()) + LB = [LB[k] for k in LK] + LI = list(range(len(LK))) + Lmod = self.module() + ambient = Lmod.is_ambient() + + if M is not None: + if not isinstance(M, Representation_abstract): + M = self.representation(M) + + MB = M.basis() + MK = list(MB.keys()) + MB = [MB[k] for k in MK] + MI = list(range(len(MK))) def sgn(k, X): """ @@ -1231,27 +1278,31 @@ def sgn(k, X): return R.one(), tuple(Y) from sage.parallel.decorate import parallel + from sage.matrix.constructor import matrix @parallel(ncpus=ncpus) def compute_diff(k): """ Build the ``k``-th differential (in parallel). """ - indices = {tuple(X): i for i,X in enumerate(combinations(Ind, k-1))} + # The indices for the exterior algebra + ext_ind = {tuple(X): i for i, X in enumerate(combinations(LI, k-1))} + # The indices for the tensor product + + # Compute the part independent of the module first ("part 2" of the computation) if sparse: - data = {} + p2_data = {} row = 0 else: - data = [] + p2_data = [] if not sparse: - zero = [zero] * len(indices) - for X in combinations(Ind, k): + zv = [zero] * len(ext_ind) + for X in combinations(LI, k): if not sparse: - ret = list(zero) + ret = list(zv) for i in range(k): Y = list(X) Y.pop(i) - # We do mone**i because we are 0-based # This is where we would do the action on # the coefficients module #ret[indices[tuple(Y)]] += mone**i * zero @@ -1259,41 +1310,89 @@ def compute_diff(k): # We shift j by 1 because we already removed # an earlier element from X. Z = tuple(Y[:j-1] + Y[j:]) - elt = mone**(i+j) * B[X[i]].bracket(B[X[j]]) + elt = mone**(i+j+1) * LB[X[i]].bracket(LB[X[j]]) + if not elt: + continue if ambient: vec = elt.to_vector() else: - vec = M.coordinate_vector(elt.to_vector()) + vec = Lmod.coordinate_vector(elt.to_vector()) for key, coeff in vec.iteritems(): + if not coeff: + continue s, A = sgn(key, Z) if A is None: continue if sparse: - coords = (row, indices[A]) - if coords in data: - data[coords] += s * coeff + coords = (row, ext_ind[A]) + if coords in p2_data: + p2_data[coords] += s * coeff else: - data[coords] = s * coeff + p2_data[coords] = s * coeff else: - ret[indices[A]] += s * coeff + ret[ext_ind[A]] += s * coeff if sparse: row += 1 else: - data.append(ret) - nrows = binomial(len(Ind), k) - ncols = binomial(len(Ind), k-1) + p2_data.append(ret) + + nrows = binomial(len(LI), k) + ncols = binomial(len(LI), k-1) MS = MatrixSpace(R, nrows, ncols, sparse=sparse) - ret = MS(data).transpose() + if M is None: + p2 = MS(p2_data).transpose() + p2.set_immutable() + return p2 + p2 = matrix.identity(len(MI)).tensor_product(MS(p2_data)).transpose() + + ten_ind = {tuple(Y): i for i, Y in enumerate(product(MI, ext_ind))} + + # Now compute the part from the module ("part 1") + if sparse: + p1_data = {} + row = 0 + else: + p1_data = [] + if not sparse: + zv = [zero] * len(ten_ind) + for v, X in product(MI, combinations(LI, k)): + if not sparse: + ret = list(zv) + for i in range(k): + # We do mone**i because we are 0-based + elt = mone**i * LB[X[i]] * MB[v] + if not elt: + continue + Y = X[:i] + X[i+1:] + for j in MI: + coeff = elt[MK[j]] + if not coeff: + continue + if sparse: + coords = (row, ten_ind[j, Y]) + if coords in p1_data: + p1_data[coords] += coeff + else: + p1_data[coords] = coeff + else: + ret[ten_ind[j, Y]] += coeff + if sparse: + row += 1 + else: + p1_data.append(ret) + + nrows = len(MI) * binomial(len(LI), k) + ncols = len(ten_ind) + MS = MatrixSpace(R, nrows, ncols, sparse=sparse) + ret = MS(p1_data).transpose() + p2 ret.set_immutable() return ret - chain_data = {X[0][0]: M for X, M in compute_diff(list( range(1,len(Ind)+1) ))} - from sage.homology.chain_complex import ChainComplex - try: - return ChainComplex(chain_data, degree_of_differential=-1) - except TypeError: - return chain_data + ind = list(range(1, len(LI) + 1)) + chain_data = {X[0][0]: M for X, M in compute_diff(ind)} + C = ChainComplex(chain_data, degree_of_differential=-1) + return C def homology(self, deg=None, M=None, sparse=True, ncpus=None): r""" From 7ee7aecf6aac7660fde4f3a23bd4ff794741b04f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 23 Nov 2023 16:14:40 +0900 Subject: [PATCH 002/191] Fixing doctest. --- .../categories/finite_dimensional_lie_algebras_with_basis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 8ae899bf6b7..7dd2f6c84ec 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -1197,7 +1197,7 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sage: ascii_art(n.chevalley_eilenberg_complex()) [ 0] [0 0] [-2] - 0 <-- C_0 <------ C_1 <---- C_2 <-- 0 + 0 <-- C_0 <------ C_1 <----- C_2 <-- 0 sage: L. = LieAlgebra(QQ, {('x','y'): {'y':1}}) sage: f = ({x: Matrix([[1,0],[0,0]]), y: Matrix([[0,1],[0,0]])}) From a2fc656dc3f804531b25e6982814227553ebac78 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Dec 2023 11:40:10 -0800 Subject: [PATCH 003/191] build/pkgs/python3/spkg-configure.m4, m4/sage_check_python_for_venv.m4: Use setuptools instead of distutils for extensions tests on Python >= 3.12 --- build/pkgs/python3/SPKG.rst | 9 ++++----- build/pkgs/python3/spkg-configure.m4 | 6 +++--- m4/sage_check_python_for_venv.m4 | 28 +++++++++++++++++++++------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/build/pkgs/python3/SPKG.rst b/build/pkgs/python3/SPKG.rst index 73c9f958f26..cb4b634b677 100644 --- a/build/pkgs/python3/SPKG.rst +++ b/build/pkgs/python3/SPKG.rst @@ -8,16 +8,15 @@ By default, Sage will try to use system's ``python3`` to set up a virtual environment, a.k.a. `venv `_ rather than building a Python 3 installation from scratch. -Sage will accept versions 3.9.x to 3.10.x. +Sage will accept versions 3.9.x to 3.11.x. You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use ``/path/to/python3_binary`` to set up the venv. Note that setting up the venv requires a number of Python modules to be available within the Python in question. Currently, -as of Sage 9.7, these modules are as follows: ``sqlite3``, ``ctypes``, ``math``, -``hashlib``, ``crypt``, ``socket``, ``zlib``, ``distutils.core``, ``ssl`` - -they will be checked for by the ``configure`` script. +as of Sage 10.2, these modules are as follows: ``sqlite3``, ``ctypes``, ``math``, +``hashlib``, ``crypt``, ``socket``, ``zlib``, ``ssl``, ``ensurepip``. -Use the ``configure`` option ``--without-system-python3`` in case you want Python 3 +Use the ``configure`` option ``--without-system-python3`` if you want Python 3 built from scratch. diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index d57aa416e3a..41c88a640cc 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -24,8 +24,8 @@ SAGE_SPKG_CONFIGURE([python3], [ dnl Check if we can do venv with a system python3 dnl instead of building our own copy. dnl Trac #31160: We no longer check for readline here. - check_modules="sqlite3, ctypes, math, hashlib, crypt, socket, zlib, distutils.core, ssl, ensurepip" - AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules], [ac_cv_path_PYTHON3], [ + check_modules="sqlite3, ctypes, math, hashlib, crypt, socket, zlib, ssl, ensurepip" + AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules and setuptools/distutils], [ac_cv_path_PYTHON3], [ AS_IF([test x"$ac_path_PYTHON3" != x], [dnl checking explicitly specified $with_python AC_MSG_RESULT([]) AC_PATH_PROG([ac_path_PYTHON3], [$ac_path_PYTHON3]) @@ -40,7 +40,7 @@ SAGE_SPKG_CONFIGURE([python3], [ ac_path_PYTHON3_found=: AC_MSG_RESULT([yes]) dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK - AC_MSG_CHECKING([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules]) + AC_MSG_CHECKING([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules and setuptools/distutils]) ]) AS_IF([test -z "$ac_cv_path_PYTHON3"], [ AC_MSG_ERROR([the python3 selected using --with-python=$with_python is not suitable]) diff --git a/m4/sage_check_python_for_venv.m4 b/m4/sage_check_python_for_venv.m4 index da89e93a7ed..363f08c1138 100644 --- a/m4/sage_check_python_for_venv.m4 +++ b/m4/sage_check_python_for_venv.m4 @@ -16,11 +16,23 @@ AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ AX_COMPARE_VERSION([$python3_version], [ge], MIN_VERSION, [ AX_COMPARE_VERSION([$python3_version], [lt], LT_VERSION, [ dnl Because the system python is not used directly but rather in a venv without site-packages, - dnl we test whether the module will be available in a venv. + dnl we should test whether the module will be available in a venv. dnl Otherwise, some system site-package may be providing this module to the system python. + dnl However, on Python >= 3.12, we need setuptools to run our extension compilation tests + dnl because distutils has been removed from the standard library. + AX_COMPARE_VERSION([$python3_version], [ge], [3.12.0], [ + conftest_venv_options="--system-site-packages" + distutils_core="setuptools" + distutils_extension="setuptools.extension" + ], [ + conftest_venv_options= + distutils_core="distutils.core" + distutils_extension="distutils.extension" + ]) + all_required_modules="]REQUIRED_MODULES[, $distutils_core, $distutils_extension" dnl m4_define([conftest_venv], [config-venv]) .... for debugging only rm -rf conftest_venv - AS_IF(["]PYTHON_EXE[" build/bin/sage-venv conftest_venv && conftest_venv/bin/python3 -c "import ]REQUIRED_MODULES[" 2>& ]AS_MESSAGE_LOG_FD, [ + AS_IF(["]PYTHON_EXE[" build/bin/sage-venv $conftest_venv_options conftest_venv && conftest_venv/bin/python3 -c "import $all_required_modules" 2>& ]AS_MESSAGE_LOG_FD, [ AS_VAR_SET([python3_result], [yes]) SAGE_PYTHON_CHECK_DISTUTILS([CC="$CC" CXX="$CXX" conftest_venv/bin/python3], [ SAGE_ARCHFLAGS="unset" @@ -46,7 +58,7 @@ AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ AC_MSG_RESULT([$python3_result]) ]) ], [ - AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: ]REQUIRED_MODULES) + AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: $all_required_modules]) ]) ], [ AC_MSG_RESULT([no, $python3_version is too recent]) @@ -123,8 +135,9 @@ PyInit_spam(void) ]) AC_LANG_POP([C]) cat > conftest.py < conftest.py < Date: Fri, 15 Dec 2023 12:20:21 -0800 Subject: [PATCH 004/191] build/pkgs/python3/distros: Add setuptools packages --- build/pkgs/_python3.12/distros/debian.txt | 1 - build/pkgs/python3/distros/alpine.txt | 1 + build/pkgs/python3/distros/debian.txt | 2 +- build/pkgs/python3/distros/fedora.txt | 1 + build/pkgs/python3/distros/freebsd.txt | 1 + build/pkgs/python3/distros/homebrew.txt | 1 + build/pkgs/python3/distros/macports.txt | 1 + build/pkgs/python3/distros/opensuse.txt | 1 + build/pkgs/python3/distros/void.txt | 1 + 9 files changed, 8 insertions(+), 2 deletions(-) diff --git a/build/pkgs/_python3.12/distros/debian.txt b/build/pkgs/_python3.12/distros/debian.txt index 20ac50641bf..04a63409a04 100644 --- a/build/pkgs/_python3.12/distros/debian.txt +++ b/build/pkgs/_python3.12/distros/debian.txt @@ -1,4 +1,3 @@ python3.12 python3.12-dev -python3.12-distutils python3.12-venv diff --git a/build/pkgs/python3/distros/alpine.txt b/build/pkgs/python3/distros/alpine.txt index 596ce366935..c4fe4764a15 100644 --- a/build/pkgs/python3/distros/alpine.txt +++ b/build/pkgs/python3/distros/alpine.txt @@ -1 +1,2 @@ python3-dev +py3-setuptools diff --git a/build/pkgs/python3/distros/debian.txt b/build/pkgs/python3/distros/debian.txt index 6b3ebf766dc..71f8637f37e 100644 --- a/build/pkgs/python3/distros/debian.txt +++ b/build/pkgs/python3/distros/debian.txt @@ -1,4 +1,4 @@ python3 libpython3-dev -python3-distutils +python3-setuptools python3-venv diff --git a/build/pkgs/python3/distros/fedora.txt b/build/pkgs/python3/distros/fedora.txt index 07358a92e89..ba7b5ea9b21 100644 --- a/build/pkgs/python3/distros/fedora.txt +++ b/build/pkgs/python3/distros/fedora.txt @@ -1 +1,2 @@ python3-devel +python-setuptools diff --git a/build/pkgs/python3/distros/freebsd.txt b/build/pkgs/python3/distros/freebsd.txt index e650785f225..d7bf687a0a3 100644 --- a/build/pkgs/python3/distros/freebsd.txt +++ b/build/pkgs/python3/distros/freebsd.txt @@ -1 +1,2 @@ lang/python +devel/py-setuptools diff --git a/build/pkgs/python3/distros/homebrew.txt b/build/pkgs/python3/distros/homebrew.txt index 1054bbddaff..3ae3509605d 100644 --- a/build/pkgs/python3/distros/homebrew.txt +++ b/build/pkgs/python3/distros/homebrew.txt @@ -1,2 +1,3 @@ # This installs /usr/local/bin/python3 -> python3.9 python3 +python-setuptools diff --git a/build/pkgs/python3/distros/macports.txt b/build/pkgs/python3/distros/macports.txt index 92826c681b4..60bf435e3ce 100644 --- a/build/pkgs/python3/distros/macports.txt +++ b/build/pkgs/python3/distros/macports.txt @@ -1 +1,2 @@ python310 +py-setuptools diff --git a/build/pkgs/python3/distros/opensuse.txt b/build/pkgs/python3/distros/opensuse.txt index 760ddd76f2c..de3b7dcc464 100644 --- a/build/pkgs/python3/distros/opensuse.txt +++ b/build/pkgs/python3/distros/opensuse.txt @@ -1 +1,2 @@ python3${PYTHON_MINOR}-devel +python3${PYTHON_MINOR}-setuptools diff --git a/build/pkgs/python3/distros/void.txt b/build/pkgs/python3/distros/void.txt index 07358a92e89..ab50f2d9b07 100644 --- a/build/pkgs/python3/distros/void.txt +++ b/build/pkgs/python3/distros/void.txt @@ -1 +1,2 @@ python3-devel +python3-setuptools From bcbf016b8fe1561178b9f7d2353130fd501fe26c Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 19 Dec 2022 18:27:56 +0800 Subject: [PATCH 005/191] compute isomorphisms between quaternion orders --- .../algebras/quatalg/quaternion_algebra.py | 109 +++++++++++++++++- .../quatalg/quaternion_algebra_element.pyx | 24 ++++ src/sage/rings/morphism.pyx | 3 + 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 9c1562041e4..9648bdd15a2 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -11,6 +11,8 @@ - Peter Bruin (2021): do not require the base ring to be a field +- Lorenz Panny (2022): :meth:`QuaternionOrder.isomorphism_to` + This code is partly based on Sage code by David Kohel from 2005. TESTS: @@ -62,6 +64,7 @@ from sage.structure.factory import UniqueFactory from sage.modules.free_module import FreeModule from sage.modules.free_module_element import vector +from sage.quadratic_forms.quadratic_form import QuadraticForm from operator import itemgetter @@ -1965,13 +1968,116 @@ def ternary_quadratic_form(self, include_basis=False): G = tr0.intersection(S) B = [Q(a) for a in G.basis()] m = matrix(QQ, [[x.pair(y) for x in B] for y in B]) - from sage.quadratic_forms.quadratic_form import QuadraticForm Q = QuadraticForm(m) if include_basis: return Q, B else: return Q + def isomorphism_to(self, other, *, conjugator=False): + r""" + Compute an isomorphism from this quaternion order `O` + to another order `O'` in the same quaternion algebra. + + If the optional keyword argument ``conjugator`` is set + to ``True``, this method returns a single quaternion + `\gamma \in O \cap O'` of minimal norm such that + `O' = \gamma^{-1} O \gamma`, + rather than the ring isomorphism it defines. + + EXAMPLES:: + + sage: Quat. = QuaternionAlgebra(-1, -19) + sage: O0 = Quat.quaternion_order([1, i, (i+j)/2, (1+k)/2]) + sage: O1 = Quat.quaternion_order([1, 667*i, 1/2+j/2+9*i, (222075/2*i+333*j+k/2)/667]) + sage: iso = O0.isomorphism_to(O1) + sage: iso + Ring morphism: + From: Order of Quaternion Algebra (-1, -19) with base ring Rational Field with basis (1, i, 1/2*i + 1/2*j, 1/2 + 1/2*k) + To: Order of Quaternion Algebra (-1, -19) with base ring Rational Field with basis (1, 667*i, 1/2 + 9*i + 1/2*j, 222075/1334*i + 333/667*j + 1/1334*k) + Defn: i |--> 629/667*i + 36/667*j - 36/667*k + j |--> 684/667*i - 648/667*j - 19/667*k + k |--> -684/667*i - 19/667*j - 648/667*k + sage: iso(1) + 1 + sage: iso(i) + 629/667*i + 36/667*j - 36/667*k + sage: iso(i/3) + Traceback (most recent call last): + ... + TypeError: 1/3*i fails to convert into the map's domain ... + + :: + + sage: gamma = O0.isomorphism_to(O1, conjugator=True); gamma + -36*i - j + k + sage: gamma in O0 + True + sage: gamma in O1 + True + sage: O1.unit_ideal() == ~gamma * O0 * gamma + True + + TESTS: + + Some random testing:: + + sage: q = randrange(1,1000) + sage: p = randrange(1,1000) + sage: Quat. = QuaternionAlgebra(-q, -p) + sage: O0 = Quat.maximal_order() + sage: while True: + ....: b = Quat.random_element() + ....: if gcd(b.reduced_norm(), Quat.discriminant()) == 1: + ....: break + sage: O1 = (b * O0).left_order() + sage: iso = O0.isomorphism_to(O1); iso + Ring morphism: ... + sage: iso.domain() == O0 + True + sage: iso.codomain() == O1 + True + sage: iso(O0.random_element()) in O1 + True + sage: iso(1) == 1 + True + sage: els = list(O0.basis()) + sage: els += [O0.random_element() for _ in range(5)] + sage: {iso(g).reduced_norm() == g.reduced_norm() for g in els} + {True} + sage: {iso(g).reduced_trace() == g.reduced_trace() for g in els} + {True} + sage: {iso(g * h) == iso(g) * iso(h) for g in els for h in els} + {True} + + ALGORITHM: + + Find a generator of the principal lattice `N\cdot O\cdot O'` + where `N = [O : O \cap O']` by minimizing the associated + quadratic form. + An isomorphism is given by conjugation by such an element. + """ + if not isinstance(other, QuaternionOrder): + raise TypeError('not a quaternion order') + Q = self.quaternion_algebra() + if other.quaternion_algebra() != Q: + raise TypeError('not an order in the same quaternion algebra') + + N = self.intersection(other).free_module().index_in(self.free_module()) + I = N * self * other + f = I.quadratic_form() + _,v = f.__pari__().qfminim(None, None, 1) + gamma = sum(ZZ(c)*g for c,g in zip(v, I.basis())) + if self*gamma != I: + raise ValueError('quaternion orders not isomorphic') + assert gamma*other == I + + if conjugator: + return gamma + + ims = [~gamma * gen * gamma for gen in Q.gens()] + return self.hom(ims, other, check=False) + class QuaternionFractionalIdeal(Ideal_fractional): pass @@ -2452,7 +2558,6 @@ def quadratic_form(self): sage: I.theta_series(10) 1 + 12*q^2 + 12*q^3 + 12*q^4 + 12*q^5 + 24*q^6 + 24*q^7 + 36*q^8 + 36*q^9 + O(q^10) """ - from sage.quadratic_forms.quadratic_form import QuadraticForm # first get the gram matrix gram_matrix = self.gram_matrix() # rescale so that there are no denominators diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index b4c5eed5b49..05d256e4079 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -683,6 +683,30 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): """ return (self.conjugate() * right).reduced_trace() + def _im_gens_(self, codomain, im_gens, base_map): + r""" + Return the image of this quaternion under the morphism + defined by ``im_gens`` in ``codomain``, where elements + of the base ring are mapped by ``base_map``. + + EXAMPLES:: + + sage: Quat. = QuaternionAlgebra(-1, -19) + sage: b = 5 + 6*i + 7*j + 8*k + sage: ims = [~b * g * b for g in Quat.gens()] + sage: Quat(1)._im_gens_(Quat, ims, None) == 1 + True + sage: i._im_gens_(Quat, ims, None) == ims[0] + True + sage: j._im_gens_(Quat, ims, None) == ims[1] + True + sage: k._im_gens_(Quat, ims, None) == ims[2] + True + """ + if base_map is None: + base_map = lambda v: v + return sum(base_map(c)*g for c,g in zip(self, [1] + list(im_gens))) + cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract): """ diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index c65f75a248c..5c945bca692 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -410,6 +410,7 @@ from sage.rings import ideal import sage.structure.all from sage.structure.richcmp cimport (richcmp, rich_to_bool) from sage.misc.cachefunc import cached_method +from sage.categories.facade_sets import FacadeSets cdef class RingMap(Morphism): @@ -1951,6 +1952,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): y |--> x + y """ D = self.domain() + if D in FacadeSets(): + D, = D.facade_for() ig = self._im_gens s = '\n'.join('{} |--> {}'.format(D.gen(i), ig[i]) for i in range(D.ngens())) From 834cf484d2fc18b8b844073aa1ae83c7abbae843 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 20 Dec 2022 08:49:28 +0800 Subject: [PATCH 006/191] factor out .minimal_element() method --- .../algebras/quatalg/quaternion_algebra.py | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 9648bdd15a2..94b25f1b478 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -11,7 +11,8 @@ - Peter Bruin (2021): do not require the base ring to be a field -- Lorenz Panny (2022): :meth:`QuaternionOrder.isomorphism_to` +- Lorenz Panny (2022): :meth:`QuaternionOrder.isomorphism_to`, + :meth:`QuaternionFractionalIdeal_rational.minimal_element` This code is partly based on Sage code by David Kohel from 2005. @@ -2053,8 +2054,8 @@ def isomorphism_to(self, other, *, conjugator=False): ALGORITHM: Find a generator of the principal lattice `N\cdot O\cdot O'` - where `N = [O : O \cap O']` by minimizing the associated - quadratic form. + where `N = [O : O cap O']` using + :meth:`QuaternionFractionalIdeal_rational.minimal_element()`. An isomorphism is given by conjugation by such an element. """ if not isinstance(other, QuaternionOrder): @@ -2065,9 +2066,7 @@ def isomorphism_to(self, other, *, conjugator=False): N = self.intersection(other).free_module().index_in(self.free_module()) I = N * self * other - f = I.quadratic_form() - _,v = f.__pari__().qfminim(None, None, 1) - gamma = sum(ZZ(c)*g for c,g in zip(v, I.basis())) + gamma = I.minimal_element() if self*gamma != I: raise ValueError('quaternion orders not isomorphic') assert gamma*other == I @@ -2569,6 +2568,32 @@ def quadratic_form(self): # now get the quadratic form return QuadraticForm(gram_matrix) + def minimal_element(self): + r""" + Return an element in this quaternion ideal of minimal norm. + + If the ideal is a principal lattice, this method can be used + to find a generator. + + EXAMPLES:: + + sage: Quat. = QuaternionAlgebra(-3,-101) + sage: O = Quat.maximal_order(); O + Order of Quaternion Algebra (-3, -101) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) + sage: (O * 5).minimal_element() + 5/2 + 5/2*i + sage: alpha = 1/2 + 1/6*i + j + 55/3*k + sage: I = O*141 + O*alpha; I.norm() + 141 + sage: el = I.minimal_element(); el + 13/2 - 7/6*i + j + 2/3*k + sage: el.reduced_norm() + 282 + """ + parif = self.quadratic_form().__pari__() + _,v = parif.qfminim(None, None, 1) + return sum(ZZ(c)*g for c,g in zip(v, self.basis())) + def theta_series(self, B, var='q'): r""" Return normalized theta series of ``self``, as a power series over From 75242a22e99aa5f1d65df4c1d4c05726ed1855d5 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 1 Jan 2024 14:59:27 +0100 Subject: [PATCH 007/191] minimizing the norm form can only work for definite quaternion algebras --- src/doc/en/reference/references/index.rst | 4 ++++ src/sage/algebras/quatalg/quaternion_algebra.py | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3f207ec0539..545d9413118 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3687,6 +3687,10 @@ REFERENCES: class number*, Habilitationsschrift, RWTH Aachen University, 2016. http://www.math.rwth-aachen.de/~Markus.Kirschmer/papers/herm.pdf +.. [KV2010] \M. Kirschmer, J. Voight: *Algorithmic enumeration of ideal classes + for quaternion orders*. SIAM J. Comput. 39(5): 1714-1747 (2010) + https://math.dartmouth.edu/~jvoight/articles/73446.pdf + .. [KB1983] \W. Kühnel and T. F. Banchoff, "The 9-vertex complex projective plane", Math. Intelligencer 5 (1983), no. 3, 11-22. diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 94b25f1b478..3c891354ae1 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1986,6 +1986,12 @@ def isomorphism_to(self, other, *, conjugator=False): `O' = \gamma^{-1} O \gamma`, rather than the ring isomorphism it defines. + .. NOTE:: + + This method is currently only implemented for definite + quaternion orders over `\QQ`. For a general algorithm, + see [KV2010]_ (Problem ``IsConjugate``). + EXAMPLES:: sage: Quat. = QuaternionAlgebra(-1, -19) @@ -2064,6 +2070,9 @@ def isomorphism_to(self, other, *, conjugator=False): if other.quaternion_algebra() != Q: raise TypeError('not an order in the same quaternion algebra') + if not self.quadratic_form().is_positive_definite(): + raise NotImplementedError('only implemented for definite quaternion orders') + N = self.intersection(other).free_module().index_in(self.free_module()) I = N * self * other gamma = I.minimal_element() @@ -2590,8 +2599,11 @@ def minimal_element(self): sage: el.reduced_norm() 282 """ - parif = self.quadratic_form().__pari__() - _,v = parif.qfminim(None, None, 1) + qf = self.quadratic_form() + if not qf.is_positive_definite(): + raise ValueError('quaternion algebra must be definite') + pariqf = qf.__pari__() + _,v = pariqf.qfminim(None, None, 1) return sum(ZZ(c)*g for c,g in zip(v, self.basis())) def theta_series(self, B, var='q'): From 4af1af57a91fbdf1f1a4dae10c97542b14b65a4f Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 1 Jan 2024 15:29:32 +0100 Subject: [PATCH 008/191] =?UTF-8?q?the=20lattice=20O=C2=B7O'=20may=20not?= =?UTF-8?q?=20form=20a=20connecting=20ideal=20for=20non-maximal=20orders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/algebras/quatalg/quaternion_algebra.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 3c891354ae1..b6901901536 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1988,8 +1988,8 @@ def isomorphism_to(self, other, *, conjugator=False): .. NOTE:: - This method is currently only implemented for definite - quaternion orders over `\QQ`. For a general algorithm, + This method is currently only implemented for maximal orders in + definite quaternion orders over `\QQ`. For a general algorithm, see [KV2010]_ (Problem ``IsConjugate``). EXAMPLES:: @@ -2072,6 +2072,8 @@ def isomorphism_to(self, other, *, conjugator=False): if not self.quadratic_form().is_positive_definite(): raise NotImplementedError('only implemented for definite quaternion orders') + if not (self.discriminant() == Q.discriminant() == other.discriminant()): + raise NotImplementedError('only implemented for maximal orders') N = self.intersection(other).free_module().index_in(self.free_module()) I = N * self * other From d073325bb8683333dff759828bf0788896d42b83 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 25 Nov 2022 23:25:30 +0800 Subject: [PATCH 009/191] add order_from_multiple() to sage.groups.all --- src/sage/groups/all.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/all.py b/src/sage/groups/all.py index 4361403dccc..5fa9ccfac39 100644 --- a/src/sage/groups/all.py +++ b/src/sage/groups/all.py @@ -8,7 +8,7 @@ from .perm_gps.all import * from .generic import (discrete_log, discrete_log_rho, discrete_log_lambda, - linear_relation, multiple, multiples) + linear_relation, multiple, multiples, order_from_multiple) lazy_import('sage.groups.class_function', 'ClassFunction') From 72d48e47f6f1e6ec6a8b4d2222def3f2bcc9e01f Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 4 Jan 2024 16:55:04 +0100 Subject: [PATCH 010/191] upgrade_database_knotinfo_2024.1.1 initial --- build/pkgs/database_knotinfo/checksums.ini | 6 +- .../database_knotinfo/package-version.txt | 2 +- src/sage/databases/knotinfo_db.py | 62 +++--- src/sage/knots/all.py | 3 +- src/sage/knots/knotinfo.py | 179 ++++++++---------- src/sage/knots/link.py | 56 +++--- 6 files changed, 131 insertions(+), 177 deletions(-) diff --git a/build/pkgs/database_knotinfo/checksums.ini b/build/pkgs/database_knotinfo/checksums.ini index fe398cb7354..2c43319eee6 100644 --- a/build/pkgs/database_knotinfo/checksums.ini +++ b/build/pkgs/database_knotinfo/checksums.ini @@ -1,5 +1,5 @@ tarball=database_knotinfo-VERSION.tar.gz -sha1=1c6746a2742b5fb5c6a2803249dd6988d6ec1890 -md5=b2cd3295f179fbbde2a970ba7f0c3db4 -cksum=1079889953 +sha1=f4b7df2f518f007d9b145bd9660c09a50c53743a +md5=62c6829c33298088a1bc0035c2b1a4b0 +cksum=401342757 upstream_url=https://pypi.io/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz diff --git a/build/pkgs/database_knotinfo/package-version.txt b/build/pkgs/database_knotinfo/package-version.txt index 603f3a6d51a..b8af7d7d7fc 100644 --- a/build/pkgs/database_knotinfo/package-version.txt +++ b/build/pkgs/database_knotinfo/package-version.txt @@ -1 +1 @@ -2023.6.1 +2024.1.1 diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index 72e39796965..3360b3bfdeb 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -43,6 +43,8 @@ from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method +columns_white_list = ['knot_atlas_anon', 'knotilus_page_anon'] +columns_black_list = ['homfly_polynomial_old'] class KnotInfoColumnTypes(Enum): r""" @@ -64,6 +66,7 @@ class KnotInfoColumnTypes(Enum): KnotsAndLinks = 'B' # column that is only used in both tables + class KnotInfoColumns(Enum): r""" Enum class to select a column from the table of knots and links provided @@ -92,6 +95,7 @@ class KnotInfoColumns(Enum): 'Quasipositive Braid', 'Multivariable Alexander Polynomial', 'HOMFLYPT Polynomial', + 'Khovanov Polynomial', 'Unoriented', 'Arc Notation', 'Linking Matrix', @@ -536,7 +540,11 @@ def _create_col_dict_sobj(self, sobj_path=None): for col in knot_column_names: name = knot_column_names[col] - if not name and col not in ['knot_atlas_anon', 'knotilus_page_anon']: + if not name and col not in columns_white_list: + # not of interest + continue + + if col in columns_black_list: # not of interest continue @@ -551,7 +559,11 @@ def _create_col_dict_sobj(self, sobj_path=None): for col in link_column_names: name = link_column_names[col] - if not name and col not in ['knot_atlas_anon', 'knotilus_page_anon']: + if not name and col not in columns_white_list: + # not of interest + continue + + if col in columns_black_list: # not of interest continue @@ -629,7 +641,7 @@ def _create_data_sobj(self, sobj_path=None): @cached_method def columns(self): r""" - Return the columns ot the databese table as instances of enum class + Return the columns ot the database table as instances of enum class :class:`KnotInfoColumns`. EXAMPLES:: @@ -737,7 +749,7 @@ def read_num_knots(self): sage: from sage.databases.knotinfo_db import KnotInfoDataBase sage: ki_db = KnotInfoDataBase() sage: ki_db.read_num_knots() # optional - database_knotinfo - 2978 + 12966 """ if not self._num_knots: self.demo_version() @@ -823,8 +835,7 @@ def _test_database(self, **options): 'homfly_polynomial': ['HOMFLY', KnotInfoColumnTypes.OnlyKnots], 'homflypt_polynomial': ['HOMFLYPT Polynomial', KnotInfoColumnTypes.OnlyLinks], 'kauffman_polynomial': ['Kauffman', KnotInfoColumnTypes.KnotsAndLinks], - 'khovanov_polynomial': ['Khovanov', KnotInfoColumnTypes.KnotsAndLinks], - 'khovanov_torsion_polynomial': ['Khovanov Torsion', KnotInfoColumnTypes.OnlyKnots], + 'khovanov_polynomial': ['Khovanov', KnotInfoColumnTypes.OnlyLinks], 'khovanov_unreduced_integral_polynomial': ['KH Unred Z Poly', KnotInfoColumnTypes.OnlyKnots], 'khovanov_reduced_integral_polynomial': ['KH Red Z Poly', KnotInfoColumnTypes.OnlyKnots], 'khovanov_reduced_rational_polynomial': ['KH Red Q Poly', KnotInfoColumnTypes.OnlyKnots], @@ -1020,15 +1031,15 @@ def _test_database(self, **options): ], dc.homfly_polynomial: [ '', - '(2*v^2-v^4)+(v^2)*z^2', - '(v^(-2)-1+ v^2)+ (-1)*z^2', - '(3*v^4-2*v^6)+ (4*v^4-v^6)*z^2+ (v^4)*z^4', - '(v^2+ v^4-v^6)+ (v^2+ v^4)*z^2', - '(v^(-2)-v^2+ v^4)+ (-1-v^2)*z^2', - '(2-2*v^2+ v^4)+ (1-3*v^2+ v^4)*z^2+ (-v^2)*z^4', - '(-v^(-2)+ 3-v^2)+ (-v^(-2)+ 3-v^2)*z^2+ (1)*z^4', - '(4*v^6-3*v^8)+ (10*v^6-4*v^8)*z^2+ (6*v^6-v^8)*z^4+ (v^6)*z^6', - '(v^2+ v^6-v^8)+ (v^2+ v^4+ v^6)*z^2' + '(2*v^2-v^4)+v^2*z^2', + '(v^(-2)-1+v^2)-z^2', + '(3*v^4-2*v^6)+(4*v^4-v^6)*z^2+v^4*z^4', + '(v^2+v^4-v^6)+(v^2+v^4)*z^2', + '(v^(-2)-v^2+v^4)+(-1-v^2)*z^2', + '(2-2*v^2+v^4)+(1-3*v^2+v^4)*z^2-v^2*z^4', + '(-v^(-2)+3-v^2)+(-v^(-2)+3-v^2)*z^2+z^4', + '(4*v^6-3*v^8)+(10*v^6-4*v^8)*z^2+(6*v^6-v^8)*z^4+v^6*z^6', + '(v^2+v^6-v^8)+(v^2+v^4+v^6)*z^2' ], dc.homflypt_polynomial: [ '1/(v^3*z)-1/(v*z)-z/v', @@ -1123,16 +1134,6 @@ def _test_database(self, **options): '3*z + 2*z^3', '-3*z-4*z^3-z^5'], dc.khovanov_polynomial: [ - '', - 'q^(-9)t^(-3)+q^(-5)t^(-2)+q^(-3)+q^(-1)', - 'q^(-5)t^(-2)+q^(-1)t^(-1)+q+q^(-1)+qt+q^5t^2', - 'q^(-15)t^(-5)+q^(-11)t^(-4)+q^(-11)t^(-3)+q^(-7)t^(-2)+q^(-5)+q^(-3)', - 'q^(-13)t^(-5)+q^(-9)t^(-4)+q^(-9)t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)', - 'q^(-9)t^(-4)+q^(-5)t^(-3)+q^(-5)t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+q^(-1)+qt+q^5t^2', - 'q^(-11)t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+(q^(-5)+q^(-3))t^(-1)+q^(-3)+2q^(-1)+tq^(-1)+q^3t^2', - 'q^(-7)t^(-3)+(q^(-5)+q^(-3))t^(-2)+(q^(-3)+q^(-1))t^(-1)+2q+2q^(-1)+t(q+q^3)+(q^3+q^5)t^2+q^7t^3', - 'q^(-21)t^(-7)+q^(-17)t^(-6)+q^(-17)t^(-5)+q^(-13)t^(-4)+q^(-13)t^(-3)+q^(-9)t^(-2)+q^(-7)+q^(-5)', - 'q^(-17)t^(-7)+q^(-13)t^(-6)+q^(-13)t^(-5)+(q^(-11)+q^(-9))t^(-4)+(q^(-9)+q^(-7))t^(-3)+(q^(-7)+q^(-5))t^(-2)+q^(-3)t^(-1)+q^(-3)+q^(-1)', '1 + q^(-2) + 1/(q^6*t^2) + 1/(q^4*t^2)', '1 + q^2 + q^4*t^2 + q^6*t^2', '1 + q^(-2) + 1/(q^10*t^4) + 1/(q^8*t^4) + 1/(q^6*t^2) + 1/(q^2*t)', @@ -1144,17 +1145,6 @@ def _test_database(self, **options): 'q^(-4) + q^(-2) + 1/(q^16*t^6) + 1/(q^14*t^6) + 1/(q^14*t^5) + 1/(q^12*t^4) + 1/(q^10*t^4) + 1/(q^10*t^3) + 1/(q^8*t^3) + 1/(q^8*t^2) + 1/(q^6*t^2) + 1/(q^4*t)', 'q^2 + q^4 + q^4*t + q^6*t^2 + q^8*t^2 + q^8*t^3 + q^10*t^3 + q^10*t^4 + q^12*t^4 + q^14*t^5 + q^14*t^6 + q^16*t^6', 'q^(-6) + q^(-4) + 1/(q^18*t^6) + 1/(q^16*t^6) + 1/(q^16*t^5) + 1/(q^12*t^4) + 1/(q^12*t^3) + 1/(q^8*t^2)'], - dc.khovanov_torsion_polynomial: [ - '', - 'Q^(-7)t^(-2)', - 'Q^(-3)t^(-1)+Q^3t^2', - 'Q^(-13)t^(-4)+Q^(-9)t^(-2)', - 'Q^(-11)t^(-4)+Q^(-7)t^(-2)+Q^(-5)t^(-1)', - 'Q^(-7)t^(-3)+Q^(-3)t^(-1)+Q^(-1)+Q^3t^2', - 'Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)+Q^(-3)+Qt^2', - 'Q^(-5)t^(-2)+Q^(-3)t^(-1)+Q^(-1)+Qt+Q^3t^2+Q^5t^3', - 'Q^(-19)t^(-6)+Q^(-15)t^(-4)+Q^(-11)t^(-2)', - 'Q^(-15)t^(-6)+Q^(-11)t^(-4)+Q^(-9)t^(-3)+Q^(-7)t^(-2)+Q^(-5)t^(-1)'], dc.khovanov_unreduced_integral_polynomial: [ '', 'q + q^(3) + t^(2) q^(5) + t^(3) q^(9) + t^(3) q^(7) T^(2)', diff --git a/src/sage/knots/all.py b/src/sage/knots/all.py index 02223efef57..d25acbda1a3 100644 --- a/src/sage/knots/all.py +++ b/src/sage/knots/all.py @@ -3,5 +3,4 @@ lazy_import('sage.knots.knot', ['Knot', 'Knots']) lazy_import('sage.knots.link', 'Link') -if DatabaseKnotInfo().is_present(): - lazy_import('sage.knots.knotinfo', ['KnotInfo', 'KnotInfoSeries']) +lazy_import('sage.knots.knotinfo', ['KnotInfo', 'KnotInfoSeries']) diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index 0ef601e124c..f6b7ccb3dc1 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -44,14 +44,12 @@ :arxiv:`Gittings, T., "Minimum Braids: A Complete Invariant of Knots and Links" `). Furthermore, note that not all columns available in the database are visible on the web -pages (see also the related note under :meth:`KnotInfoBase.khovanov_polynomial`). -It is planned to remove non-visible columns from the database in the future (see +pages. It is planned to remove non-visible columns from the database in the future (see the `Python Wrapper `__ for updated information). EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.pd_notation() [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]] @@ -260,13 +258,15 @@ def eval_knotinfo(string, locals={}, to_tuple=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo, eval_knotinfo + sage: from sage.knots.knotinfo import eval_knotinfo sage: L = KnotInfo.L4a1_0 sage: L.braid_notation(original=True) '{3, {-2, -2, -1, 2, -1}}' sage: eval_knotinfo(_) (3, (-2, -2, -1, 2, -1)) """ + if not string: + raise NotImplementedError('This value is not provided by the database') if to_tuple: new_string = string.replace('{', '(') new_string = new_string.replace('}', ')') @@ -276,6 +276,29 @@ def eval_knotinfo(string, locals={}, to_tuple=True): new_string = new_string.replace(';', ',') return sage_eval(new_string, locals=locals) +def knotinfo_int(string): + r""" + Preparse a string from the KnotInfo database representing an integer. + + INPUT: + + - ``string`` -- string that gives a value of some database entry + + EXAMPLES:: + + sage: from sage.knots.knotinfo import knotinfo_int + sage: knotinfo_int('7') + 7 + sage: knotinfo_int('') + Traceback (most recent call last): + ... + NotImplementedError: This integer is not provided by the database + """ + if not string: + raise NotImplementedError('This integer is not provided by the database') + else: + return int(string) + def knotinfo_bool(string): r""" Preparse a string from the KnotInfo database representing a boolean. @@ -290,13 +313,14 @@ def knotinfo_bool(string): sage: knotinfo_bool('Y') True """ + if not string: + raise NotImplementedError('This boolean is not provided by the database') if string == 'Y': return True elif string == 'N': return False raise ValueError('%s is not a KnotInfo boolean') - # --------------------------------------------------------------------------------- # KnotInfoBase # --------------------------------------------------------------------------------- @@ -307,7 +331,6 @@ class KnotInfoBase(Enum): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: [knot.name for knot in KnotInfo if knot.crossing_number() < 5] ['K0_1', 'K3_1', 'K4_1', 'L2a1_0', 'L2a1_1', 'L4a1_0', 'L4a1_1'] @@ -330,7 +353,6 @@ def __gt__(self, other): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0 < KnotInfo.L4a1_1 # indirect doctest True sage: KnotInfo.L2a1_0 < KnotInfo.K3_1 # indirect doctest @@ -351,7 +373,6 @@ def items(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: it = L.items sage: [i.name for i in it if i.name.startswith('braid')] @@ -378,7 +399,6 @@ def __getitem__(self, item): r""" EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L[L.items.alternating] 'Y' @@ -416,10 +436,9 @@ def _offset_knots(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L._offset_knots() # optional - database_knotinfo - 2978 + 12966 """ return db.read_num_knots() @@ -431,12 +450,16 @@ def _braid_group(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L._braid_group() Braid group on 3 strands """ - n = self.braid_index() + try: + n = self.braid_index() + except NotImplementedError: + bn = self.braid_notation() + n = max(abs(i) for i in bn) + 1 + if n == 1: return BraidGroup(2) else: @@ -450,7 +473,6 @@ def _homfly_pol_ring(self, var1, var2): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_1 sage: L._homfly_pol_ring('u', 'v') Multivariate Laurent Polynomial Ring in u, v over Integer Ring @@ -477,7 +499,6 @@ def pd_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.pd_notation() [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]] @@ -520,7 +541,6 @@ def dt_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.dt_notation() [[6, 8], [2, 4]] @@ -563,7 +583,6 @@ def gauss_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.gauss_notation() [[1, -3, 2, -4], [3, -1, 4, -2]] @@ -606,7 +625,6 @@ def braid_notation(self, original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.braid_notation() (-2, -2, -1, 2, -1) @@ -661,17 +679,23 @@ def braid_index(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L4a1_0 sage: L.braid_index() 3 + sage: KnotInfo.K13a_1.inject() # optional - database_knotinfo + Defining K13a_1 + sage: K13a_1.braid_index() # optional - database_knotinfo + Traceback (most recent call last): + ... + NotImplementedError: This integer is not provided by the database + """ if self.is_knot(): - return int(self[self.items.braid_index]) + return knotinfo_int(self[self.items.braid_index]) else: braid_notation = self[self.items.braid_notation] braid_notation = eval_knotinfo(braid_notation) - return int(braid_notation[0]) + return knotinfo_int(braid_notation[0]) @cached_method def braid_length(self): @@ -686,12 +710,11 @@ def braid_length(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.braid_length() 3 """ - return int(self[self.items.braid_length]) + return knotinfo_int(self[self.items.braid_length]) @cached_method def braid(self): @@ -700,12 +723,13 @@ def braid(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.braid() s^3 sage: K.braid_notation() (1, 1, 1) + sage: KnotInfo.K13n_1448.braid() # optional - database_knotinfo + s0^-1*s1*s2*s3*s4*s3^2*s2^-1*s1^-1*s0*s2^-1*s1*(s3*s2)^2*s4^-1*s3*s2*s1^-1*s3*s2^-1*s3 """ return self._braid_group()(self.braid_notation()) @@ -716,7 +740,6 @@ def num_components(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L6a1_0.num_components() 2 """ @@ -737,7 +760,6 @@ def crossing_number(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.crossing_number() 4 sage: KnotInfo.K3_1.crossing_number() @@ -745,7 +767,7 @@ def crossing_number(self): sage: Link(KnotInfo.L4a1_0.braid()) Link with 2 components represented by 5 crossings """ - return int(self[self.items.crossing_number]) + return knotinfo_int(self[self.items.crossing_number]) @cached_method def determinant(self): @@ -769,7 +791,6 @@ def determinant(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.determinant() 4 sage: KnotInfo.K3_1.determinant() @@ -780,7 +801,7 @@ def determinant(self): if self.crossing_number() == 0: # see note above return 1 - return int(self[self.items.determinant]) + return knotinfo_int(self[self.items.determinant]) @cached_method def three_genus(self): @@ -797,7 +818,6 @@ def three_genus(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.three_genus() # optional - databsase_knotinfo 1 @@ -808,7 +828,7 @@ def three_genus(self): sage: KnotInfo.K5_2.link().genus() 3 """ - return int(self[self.items.three_genus]) + return knotinfo_int(self[self.items.three_genus]) @cached_method def signature(self): @@ -826,11 +846,10 @@ def signature(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.signature() # optional - databsase_knotinfo 1 """ - return int(self[self.items.signature]) + return knotinfo_int(self[self.items.signature]) @cached_method def is_knot(self): @@ -839,7 +858,6 @@ def is_knot(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L7a1_0.is_knot() # optional - database_knotinfo False sage: KnotInfo.K6_3.is_knot() @@ -855,7 +873,6 @@ def name_unoriented(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L10a122_1_0.name_unoriented() # optional - database_knotinfo 'L10a122' """ @@ -890,7 +907,6 @@ class of an oriented pair, `K = (S_3, S_1)`, with `S_i` EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K6_1.series().inject() Defining K6 sage: [(K.name, K.symmetry_type()) for K in K6] @@ -913,7 +929,6 @@ def is_reversible(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K6_3.is_reversible() True """ @@ -952,26 +967,24 @@ def is_amphicheiral(self, positive=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo - sage: Kp = KnotInfo.K12a_427 # optional - database_knotinfo - sage: Kp.is_amphicheiral() # optional - database_knotinfo + sage: # optional - database_knotinfo + sage: Kp = KnotInfo.K12a_427 + sage: Kp.is_amphicheiral() False - sage: Kp.is_amphicheiral(positive=True) # optional - database_knotinfo + sage: Kp.is_amphicheiral(positive=True) True - - sage: Kn = KnotInfo.K10_88 # optional - database_knotinfo - sage: Kn.is_amphicheiral() # optional - database_knotinfo + sage: Kn = KnotInfo.K10_88 + sage: Kn.is_amphicheiral() True - sage: Kn.is_amphicheiral(positive=True) # optional - database_knotinfo + sage: Kn.is_amphicheiral(positive=True) False - sage: KnotInfo.L4a1_0.is_amphicheiral() False - sage: KnotInfo.L10n59_1.is_amphicheiral() # optional - database_knotinfo + sage: KnotInfo.L10n59_1.is_amphicheiral() True - sage: KnotInfo.L10n36_0.inject() # optional - database_knotinfo + sage: KnotInfo.L10n36_0.inject() Defining L10n36_0 - sage: L10n36_0.is_amphicheiral() is None # optional - database_knotinfo + sage: L10n36_0.is_amphicheiral() is None True """ if self.is_knot(): @@ -1023,7 +1036,6 @@ def is_alternating(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_alternating() True """ @@ -1036,7 +1048,6 @@ def is_almost_alternating(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_almost_alternating() # optional - database_knotinfo False """ @@ -1050,7 +1061,6 @@ def is_quasi_alternating(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_quasi_alternating() # optional - database_knotinfo True """ @@ -1064,7 +1074,6 @@ def is_adequate(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_adequate() # optional - database_knotinfo True """ @@ -1078,7 +1087,6 @@ def is_positive(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_positive() True """ @@ -1091,7 +1099,6 @@ def is_quasipositive(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_quasipositive() # optional - database_knotinfo True """ @@ -1105,7 +1112,6 @@ def is_strongly_quasipositive(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_strongly_quasipositive() # optional - database_knotinfo True """ @@ -1119,7 +1125,6 @@ def is_positive_braid(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.is_positive_braid() # optional - database_knotinfo False """ @@ -1133,7 +1138,6 @@ def is_fibered(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K6_3.is_fibered() True """ @@ -1146,7 +1150,6 @@ def is_oriented(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L6a2_1.is_oriented() True """ @@ -1191,12 +1194,11 @@ def homfly_polynomial(self, var1='v', var2='z', original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K3_1 = KnotInfo.K3_1 sage: PK3_1 = K3_1.homfly_polynomial(); PK3_1 -v^4 + v^2*z^2 + 2*v^2 sage: K3_1.homfly_polynomial(original=True) - '(2*v^2-v^4)+(v^2)*z^2' + '(2*v^2-v^4)+v^2*z^2' sage: PK3_1 == K3_1.link().homfly_polynomial(normalization='vz') True @@ -1289,7 +1291,6 @@ def kauffman_polynomial(self, var1='a', var2='z', original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: L = KnotInfo.L2a1_1 sage: L.kauffman_polynomial() a^-1*z - a^-1*z^-1 + a^-2 + a^-3*z - a^-3*z^-1 @@ -1396,7 +1397,6 @@ def jones_polynomial(self, variab=None, skein_normalization=False, puiseux=False EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K4_1 sage: Kj = K.jones_polynomial(); Kj t^2 - t - 1/t + 1/t^2 + 1 @@ -1574,7 +1574,6 @@ def alexander_polynomial(self, var='t', original=False, laurent_poly=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K4_1 sage: Ka = K.alexander_polynomial(); Ka t^2 - 3*t + 1 @@ -1650,7 +1649,6 @@ def conway_polynomial(self, var='t', original=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K4_1 sage: Kc = K.conway_polynomial(); Kc -t^2 + 1 @@ -1686,7 +1684,7 @@ def conway_polynomial(self, var='t', original=False): return R(eval_knotinfo(conway_polynomial, locals=lc)) @cached_method - def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, reduced=False, odd=False, KhoHo=False): + def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, reduced=False, odd=False): r""" Return the Khovanov polynomial according to the value of column ``khovanov_polynomial`` for this knot or link as an instance of @@ -1704,10 +1702,6 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, the reduced version of the homology is used - ``odd`` -- boolean (default: ``False``); if set to ``True`` the odd version of the homology is used - - ``KhoHo`` -- boolean (default: ``False`` for knots and ``True`` - for multi-component links); if set to ``True`` the data calculated - using `KhoHo `__ is used - (see the note below) OUTPUT: @@ -1718,28 +1712,14 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, .. NOTE :: The data used for multi-component links were calculated with the program - `KhoHo `__. These can still be - used for knots by setting the optional argument ``KhoHo`` to ``True``, - even though they will no longer be visible on the Knot website as of - October 30, 2022. Otherwise, for knots data calculated with + `KhoHo `__.which uses the ``DT`` + notation. For knots data calculated with `KnotJob `__ are used. The latter program is more accurate in terms of orientation and reflection as it is based on ``PD`` code. - Note that in the future columns that are not visible on the web page may - also be removed in the database (see the - `Python wrapper `__ - for updated information). Therefore, the ``KhoHo`` option cannot be - guaranteed to work after upgrading the ``database_knotinfo``-SPKG. - - Furthermore, since the results of ``KhoHo`` were computed using the ``DT`` - notation, the Khovanov polynomial returned by this option belongs to the - mirror image of the given knot for a `list of 140 exceptions - `__. - EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K6_3 sage: Kk = K.khovanov_polynomial(); Kk q^7*t^3 + q^5*t^2 + q^3*t^2 + q^3*t + q*t + 2*q + 2*q^-1 + q^-1*t^-1 @@ -1820,12 +1800,11 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, """ ch = base_ring.characteristic() integral = ch == 0 and base_ring.is_field() + KhoHo = False if not self.is_knot(): # KnotJob calculated results only available for knots - KhoHo = True - if KhoHo: - # use the old results obtained by the KhoHo software khovanov_polynomial = self[self.items.khovanov_polynomial] + KhoHo = True else: if reduced: if integral: @@ -1932,7 +1911,6 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.link() Knot represented by 3 crossings @@ -2050,7 +2028,6 @@ def is_unique(self): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.is_unique() True sage: KnotInfo.L5a1_0.is_unique() @@ -2104,7 +2081,6 @@ def is_recoverable(self, unique=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.inject() Defining L4a1_0 sage: L4a1_0.is_recoverable() @@ -2170,7 +2146,6 @@ def inject(self, verbose=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.K5_2.inject() Defining K5_2 sage: K5_2.is_alternating() @@ -2197,7 +2172,6 @@ def series(self, oriented=False): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K5 = KnotInfo.K5_2.series() sage: K5(1) @@ -2232,7 +2206,6 @@ def diagram(self, single=False, new=0, autoraise=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.diagram() # not tested True @@ -2262,7 +2235,6 @@ def knot_atlas_webpage(self, new=0, autoraise=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.knot_atlas_webpage() # not tested True @@ -2282,7 +2254,6 @@ def knotilus_webpage(self, new=0, autoraise=True): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: K = KnotInfo.K3_1 sage: K.knotilus_webpage(new=1) # not tested True @@ -2539,16 +2510,16 @@ def __call__(self, item): sage: K6(2) # indirect doctest - sage: from sage.knots.knotinfo import KnotInfo - sage: KnotInfo.L8a21_0_1_0.inject() # optional - database_knotinfo + sage: # optional - database_knotinfo + sage: KnotInfo.L8a21_0_1_0.inject() Defining L8a21_0_1_0 - sage: L8a21_0_1_0.series().inject() # optional - database_knotinfo + sage: L8a21_0_1_0.series().inject() Defining L8a - sage: L8a(1) # optional - database_knotinfo + sage: L8a(1) Series of links L8a1 - sage: L8a(21)(2) == L8a21_0_1_0 # optional - database_knotinfo + sage: L8a(21)(2) == L8a21_0_1_0 True - sage: L8a(21)('010') == L8a21_0_1_0 # optional - database_knotinfo + sage: L8a(21)('010') == L8a21_0_1_0 True """ if self._name_unoriented: @@ -2615,7 +2586,6 @@ def is_recoverable(self, unique=True, max_samples=8): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: KnotInfo.L4a1_0.series().inject() Defining L4a sage: L4a.is_recoverable() @@ -2644,7 +2614,6 @@ def _test_recover(self, **options): EXAMPLES:: - sage: from sage.knots.knotinfo import KnotInfo sage: TestSuite(KnotInfo.L5a1_0.series()).run(verbose=True) # indirec doctest running ._test_category() . . . pass running ._test_new() . . . pass diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index eb3d51a883d..5e353f62e05 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -3955,8 +3955,8 @@ def _knotinfo_matching_list(self): # set the limits for the KnotInfoSeries if cr > 11 and co > 1: cr = 11 - if cr > 12: - cr = 12 + if cr > 13: + cr = 13 Hp = self.homfly_polynomial(normalization='vz') @@ -3995,8 +3995,9 @@ def _knotinfo_matching_list(self): if L.pd_notation() == pd_code: return [L], True # pd_notation is unique in the KnotInfo database - if L.braid_index() <= br_ind: - if self._markov_move_cmp(L.braid()): + Lbraid = L.braid() + if Lbraid.strands() <= br_ind: + if self._markov_move_cmp(Lbraid): res.append(L) if res: @@ -4058,27 +4059,29 @@ def get_knotinfo(self, mirror_version=True, unique=True): EXAMPLES:: + sage: # optional - database_knotinfo sage: from sage.knots.knotinfo import KnotInfo sage: L = Link([[4,1,5,2], [10,4,11,3], [5,17,6,16], [7,13,8,12], ....: [18,10,19,9], [2,12,3,11], [13,21,14,20], [15,7,16,6], ....: [22,17,1,18], [8,20,9,19], [21,15,22,14]]) - sage: L.get_knotinfo() # optional - database_knotinfo + sage: L.get_knotinfo() (, True) - - sage: K = KnotInfo.K10_25 # optional - database_knotinfo - sage: l = K.link() # optional - database_knotinfo - sage: l.get_knotinfo() # optional - database_knotinfo + sage: K = KnotInfo.K10_25 + sage: l = K.link() + sage: l.get_knotinfo() (, False) - Knots with more than 12 and proper links having more than 11 crossings + Knots with more than 13 and proper links having more than 11 crossings cannot be identified. In addition non prime links or even links whose HOMFLY-PT polynomial is not irreducible cannot be identified:: sage: b, = BraidGroup(2).gens() - sage: Link(b**13).get_knotinfo() + sage: Link(b**13).get_knotinfo() # optional - database_knotinfo + (, False) + sage: Link(b**14).get_knotinfo() Traceback (most recent call last): ... - NotImplementedError: this knot having more than 12 crossings cannot be determined + NotImplementedError: this link having more than 11 crossings cannot be determined sage: Link([[1, 4, 2, 5], [3, 8, 4, 1], [5, 2, 6, 3], [6, 10, 7, 9], [10, 8, 9, 7]]) Link with 2 components represented by 5 crossings @@ -4102,17 +4105,15 @@ def get_knotinfo(self, mirror_version=True, unique=True): Usage of option ``unique``:: - sage: l = K.link(K.items.gauss_notation) # optional - database_knotinfo - sage: l.get_knotinfo() # optional - database_knotinfo + sage: # optional - database_knotinfo + sage: l = K.link(K.items.gauss_notation) + sage: l.get_knotinfo() Traceback (most recent call last): ... NotImplementedError: this link cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: l.get_knotinfo(unique=False) # optional - database_knotinfo + sage: l.get_knotinfo(unique=False) [(, False), (, False)] - - sage: # optional - database_knotinfo sage: k11 = KnotInfo.K11n_82.link() sage: k11m = k11.mirror_image() sage: k11mr = k11m.reverse() @@ -4121,31 +4122,26 @@ def get_knotinfo(self, mirror_version=True, unique=True): ... NotImplementedError: mirror type of this link cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: k11mr.get_knotinfo(unique=False) # optional - database_knotinfo + sage: k11mr.get_knotinfo(unique=False) [(, '?')] - sage: t = (1, -2, 1, 1, -2, 1, -2, -2) sage: l8 = Link(BraidGroup(3)(t)) - sage: l8.get_knotinfo() # optional - database_knotinfo + sage: l8.get_knotinfo() Traceback (most recent call last): ... NotImplementedError: this link cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: l8.get_knotinfo(unique=False) # optional - database_knotinfo + sage: l8.get_knotinfo(unique=False) [(, None), (, None)] - sage: t = (2, -3, -3, -2, 3, 3, -2, 3, 1, -2, -2, 1) sage: l12 = Link(BraidGroup(5)(t)) - sage: l12.get_knotinfo() # optional - database_knotinfo + sage: l12.get_knotinfo() Traceback (most recent call last): ... NotImplementedError: this link having more than 11 crossings cannot be uniquely determined use keyword argument `unique` to obtain more details - - sage: l12.get_knotinfo(unique=False) # optional - database_knotinfo + sage: l12.get_knotinfo(unique=False) [(, '?'), (, None), (, None), @@ -4344,9 +4340,9 @@ def answer_list(l): uniq_txt = (' uniquely', non_unique_hint) cr = len(self.pd_code()) - if self.is_knot() and cr > 12: + if self.is_knot() and cr > 13: # we cannot not be sure if this link is recorded in the KnotInfo database - raise NotImplementedError('this knot having more than 12 crossings cannot be%s determined%s' % uniq_txt) + raise NotImplementedError('this knot having more than 13 crossings cannot be%s determined%s' % uniq_txt) if not self.is_knot() and cr > 11: # we cannot not be sure if this link is recorded in the KnotInfo database From 077e0f127f1c975b7bfec64a62af436ddf1cc286 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 5 Jan 2024 12:51:40 +0100 Subject: [PATCH 011/191] 37014: pycodestyle fix --- src/sage/databases/knotinfo_db.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index 3360b3bfdeb..8817a304d85 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -66,7 +66,6 @@ class KnotInfoColumnTypes(Enum): KnotsAndLinks = 'B' # column that is only used in both tables - class KnotInfoColumns(Enum): r""" Enum class to select a column from the table of knots and links provided From e16c632c39c00f9719b25f4768002b5bed44c68e Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 6 Jan 2024 13:56:38 +0100 Subject: [PATCH 012/191] add doctests for error cases, too --- .../algebras/quatalg/quaternion_algebra.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index b6901901536..b9794240d3f 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -2057,6 +2057,58 @@ def isomorphism_to(self, other, *, conjugator=False): sage: {iso(g * h) == iso(g) * iso(h) for g in els for h in els} {True} + Test error cases:: + + sage: Quat. = QuaternionAlgebra(-1,-11) + sage: O = Quat.maximal_order() + sage: O.isomorphism_to('potato') + Traceback (most recent call last): + ... + TypeError: not a quaternion order + + :: + + sage: Quat1. = QuaternionAlgebra(-1,-11) + sage: Quat2. = QuaternionAlgebra(-3,-11) + sage: Quat1.discriminant() == Quat2.discriminant() # isomorphic + True + sage: O1 = Quat1.maximal_order() + sage: O2 = Quat2.maximal_order() + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + TypeError: not an order in the same quaternion algebra + + :: + + sage: Quat. = QuaternionAlgebra(7,11) + sage: O1 = Quat.maximal_order() + sage: O2 = (O1 * (i+j)).right_order() + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + NotImplementedError: only implemented for definite quaternion orders + + :: + + sage: Quat. = QuaternionAlgebra(-1,-11) + sage: O1 = Quat.quaternion_order([1, i, j, k]) + sage: O2 = Quat.quaternion_order([1,-i, j,-k]) + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + NotImplementedError: only implemented for maximal orders + + :: + + sage: Quat. = QuaternionAlgebra(-1,-11) + sage: O1 = Quat.quaternion_order([1, i, (i+j)/2, (1+k)/2]) + sage: O2 = Quat.quaternion_order([1, (2+i+k)/4, (-11*i+2*j+k)/4, (-5*i+k)/3]) + sage: O1.isomorphism_to(O2) + Traceback (most recent call last): + ... + ValueError: quaternion orders not isomorphic + ALGORITHM: Find a generator of the principal lattice `N\cdot O\cdot O'` From fc6b33e5c8225a8259bbcd86ee8164ba20ff00b3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 19 Dec 2023 14:59:25 -0800 Subject: [PATCH 013/191] src/sage/combinat/designs/subhypergraph_search.pyx: constness fix for clang 16 --- src/sage/combinat/designs/subhypergraph_search.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index 267ff3b2fe0..c31e8dcd229 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -295,7 +295,7 @@ cdef int is_subhypergraph_admissible(hypergraph h1,hypergraph * h2_trace,int n,h return 1 -cdef int cmp_128_bits(void * a, void * b) noexcept nogil: +cdef int cmp_128_bits(const void * a, const void * b) noexcept nogil: r""" Lexicographic order on 128-bits words """ From 81bdd75f98d0a3c0a2efb37b19d22b8862fadca2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jan 2024 17:00:47 -0800 Subject: [PATCH 014/191] src/doc/en/installation/source.rst: Use '.. envvar::' to define variables --- src/doc/en/installation/source.rst | 183 ++++++++++++++++++++--------- 1 file changed, 127 insertions(+), 56 deletions(-) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 8ec90a1b022..c3052e5c5f8 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -645,6 +645,9 @@ how it is built: Environment variables --------------------- +Environment variables controlling the build process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Sage uses several environment variables to control its build process. Most users won't need to set any of these: the build process just works on many platforms. @@ -662,17 +665,17 @@ packages before building). Package downloads use the Sage mirror network, the nearest mirror will be determined automatically for you. This is influenced by the following environment variable: -- :envvar:`SAGE_SERVER` - Try the specified mirror first, before - falling back to the official Sage mirror list. Note that Sage will - search the directory - - - ``SAGE_SERVER/spkg/upstream`` +.. envvar:: SAGE_SERVER - for upstream tarballs. + Try the specified mirror first, before falling back to the official + Sage mirror list. Note that Sage will search the directory + ``SAGE_SERVER/spkg/upstream`` for upstream tarballs. Here are some of the more commonly used variables affecting the build process: -- :envvar:`MAKE` - one useful setting for this variable when building Sage is +.. envvar:: MAKE + + One useful setting for this variable when building Sage is ``MAKE='make -jNUM'`` to tell the ``make`` program to run ``NUM`` jobs in parallel when building. Note that some Sage packages may not support this variable. @@ -693,9 +696,11 @@ Here are some of the more commonly used variables affecting the build process: Some users on single-core macOS machines have reported problems when building Sage with ``MAKE='make -jNUM'`` with ``NUM`` greater than one. -- :envvar:`SAGE_NUM_THREADS` - if set to a number, then when rebuilding with - ``sage -b`` or parallel doctesting with ``sage -t -p 0``, use at most this - many threads. +.. envvar:: SAGE_NUM_THREADS + + If set to a number, then when rebuilding with ``sage -b`` or + parallel doctesting with ``sage -t -p 0``, use at most this many + threads. If this is not set, then determine the number of threads using the value of the :envvar:`MAKE` (see above) or :envvar:`MAKEFLAGS` environment variables. @@ -709,23 +714,29 @@ Here are some of the more commonly used variables affecting the build process: When ``sage -t -p`` runs under the control of the GNU ``make`` jobserver, then Sage will request as most this number of job slots. -- :envvar:`V` - if set to ``0``, silence the build. Instead of - showing a detailed compilation log, only one line of output is shown - at the beginning and at the end of the installation of each Sage - package. To see even less output, use:: +.. envvar:: V + + If set to ``0``, silence the build. Instead of showing a detailed + compilation log, only one line of output is shown at the beginning + and at the end of the installation of each Sage package. To see + even less output, use:: $ make -s V=0 (Note that the above uses the syntax of setting a Makefile variable.) -- :envvar:`SAGE_CHECK` - if set to ``yes``, then during the build process, +.. envvar:: SAGE_CHECK + + If set to ``yes``, then during the build process, or when installing packages manually, run the test suite for each package which has one, and stop with an error if tests are failing. If set to ``warn``, then only a warning is printed in this case. See also :envvar:`SAGE_CHECK_PACKAGES`. -- :envvar:`SAGE_CHECK_PACKAGES` - if :envvar:`SAGE_CHECK` is set to ``yes``, +.. envvar:: SAGE_CHECK_PACKAGES + + If :envvar:`SAGE_CHECK` is set to ``yes``, then the default behavior is to run test suites for all spkgs which contain them. If :envvar:`SAGE_CHECK_PACKAGES` is set, it should be a comma-separated list @@ -742,9 +753,13 @@ Here are some of the more commonly used variables affecting the build process: on most platforms. So when this variable is empty or unset, Sage uses a default of ``!python2,!python3``. -- :envvar:`SAGE_INSTALL_GCC` - **Obsolete, do not use, to be removed** +.. envvar:: SAGE_INSTALL_GCC + + **Obsolete, do not use, to be removed** + +.. envvar:: SAGE_INSTALL_CCACHE -- :envvar:`SAGE_INSTALL_CCACHE` - by default Sage doesn't install ccache, + By default Sage doesn't install :ref:`ccache `, however by setting ``SAGE_INSTALL_CCACHE=yes`` Sage will install ccache. Because the Sage distribution is quite large, the maximum cache is set to 4G. This can be changed by running ``sage -sh -c "ccache --max-size=SIZE"``, @@ -756,10 +771,9 @@ Here are some of the more commonly used variables affecting the build process: building ccache for Sage, so that Sage can pull down the necessary sources. -- .. _sage_debug: +.. envvar:: SAGE_DEBUG - :envvar:`SAGE_DEBUG` - controls debugging support. - There are three different possible values: + Controls debugging support. There are three different possible values: * Not set (or set to anything else than "yes" or "no"): build binaries with debugging symbols, but no special debug builds. @@ -778,12 +792,16 @@ Here are some of the more commonly used variables affecting the build process: Instead of using :envvar:`SAGE_DEBUG` one can configure with ``--enable-debug={no|symbols|yes}``. -- :envvar:`SAGE_PROFILE` - controls profiling support. If this is set +.. envvar:: SAGE_PROFILE + + Controls profiling support. If this is set to ``yes``, profiling support is enabled where possible. Note that - Python-level profiling is always available; This option enables + Python-level profiling is always available; this option enables profiling in Cython modules. -- :envvar:`SAGE_BUILD_DIR` - the default behavior is to build each spkg in a +.. envvar:: SAGE_BUILD_DIR + + The default behavior is to build each spkg in a subdirectory of :file:`$SAGE_ROOT/local/var/tmp/sage/build/`; for example, build version 7.27.0 of :file:`ipython` in the directory @@ -806,7 +824,9 @@ Here are some of the more commonly used variables affecting the build process: permission to create. The path name must contain **no spaces**. -- :envvar:`SAGE_KEEP_BUILT_SPKGS` - the default behavior is to delete each +.. envvar:: SAGE_KEEP_BUILT_SPKGS + + The default behavior is to delete each build directory -- the appropriate subdirectory of :file:`$SAGE_ROOT/local/var/tmp/sage/build` or :file:`$SAGE_BUILD_DIR` -- after each spkg @@ -841,9 +861,9 @@ Here are some of the more commonly used variables affecting the build process: So you can set this variable to ``yes`` instead of using the ``-s`` flag for ``sage -i`` and ``sage -f``. -- .. _sage_fat_binary: +.. envvar:: SAGE_FAT_BINARY - :envvar:`SAGE_FAT_BINARY` - to build binaries that will run on the + To build binaries that will run on the widest range of target CPUs set this variable to ``yes`` before building Sage or configure with ``--enable-fat-binary``. This does not make the binaries relocatable, it only @@ -851,7 +871,9 @@ Here are some of the more commonly used variables affecting the build process: be moved to a different directory) binaries, you must use https://github.com/sagemath/binary-pkg -- :envvar:`SAGE_SUDO` - set this to ``sudo -E`` or to any other +.. envvar:: SAGE_SUDO + + Set this to ``sudo -E`` or to any other command prefix that is necessary to write into a installation hierarchy (:envvar:`SAGE_LOCAL`) owned by root or another user. Note that this command needs to preserve environment variable @@ -864,9 +886,12 @@ Here are some of the more commonly used variables affecting the build process: supports :envvar:`SAGE_SUDO`, into a root-owned installation hierarchy (:envvar:`SAGE_LOCAL`). -Environment variables for documentation build: +Environment variables for documentation build +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. envvar:: SAGE_DOCBUILD_OPTS -- :envvar:`SAGE_DOCBUILD_OPTS` - the value of this variable is passed as an + The value of this variable is passed as an argument to ``sage --docbuild all html`` or ``sage --docbuild all pdf`` when you run ``make``, ``make doc``, or ``make doc-pdf``. For example, you can add ``--no-plot`` to this variable to avoid building the graphics coming from @@ -874,18 +899,24 @@ Environment variables for documentation build: ``--include-tests-blocks`` to include all "TESTS" blocks in the reference manual. Run ``sage --docbuild help`` to see the full list of options. -- :envvar:`SAGE_SPKG_INSTALL_DOCS` - if set to ``yes``, then install +.. envvar:: SAGE_SPKG_INSTALL_DOCS + + If set to ``yes``, then install package-specific documentation to :file:`$SAGE_ROOT/local/share/doc/PACKAGE_NAME/` when an spkg is installed. This option may not be supported by all spkgs. Some spkgs might also assume that certain programs are available on the system (for example, ``latex`` or ``pdflatex``). -- :envvar:`SAGE_USE_CDNS` -- if set to ``yes``, then build the documentation +.. envvar:: SAGE_USE_CDNS + + If set to ``yes``, then build the documentation using CDNs (Content Distribution Networks) for scripts necessary for HTML documentation, such as `MathJax `_. -- :envvar:`SAGE_LIVE_DOC` -- if set to ``yes``, then build live Sage +.. envvar:: SAGE_LIVE_DOC + + If set to ``yes``, then build live Sage documentation. If the ``Make live`` button on any webpage of the live doc is clicked, every example code gets a `CodeMirror `_ code cell runnable via `Thebe `_. @@ -895,9 +926,10 @@ Environment variables for documentation build: local Jupyter server. The environment variable :envvar:`SAGE_JUPYTER_SERVER` is used for this purpose. - :envvar:`SAGE_JUPYTER_SERVER` - set this to either ``binder``, - ``binder:repo`` with ``repo`` specifying a Binder repo or the URL to a local - Jupyter server. +.. envvar:: SAGE_JUPYTER_SERVER + + Set this to either ``binder``, ``binder:repo`` with ``repo`` + specifying a Binder repo or the URL to a local Jupyter server. - ``binder`` refers to `Sage's official Binder repo `_. This is assumed if the @@ -925,45 +957,68 @@ Environment variables for documentation build: before opening the Sage documentation webpage. -Environment variables dealing with specific Sage packages: +Environment variables dealing with specific Sage packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- :envvar:`SAGE_MATPLOTLIB_GUI` - if set to anything non-empty except ``no``, +.. envvar:: SAGE_MATPLOTLIB_GUI + + If set to anything non-empty except ``no``, then Sage will attempt to build the graphical backend when it builds the matplotlib package. -- :envvar:`PARI_CONFIGURE` - use this to pass extra parameters to +.. envvar:: OPENBLAS_CONFIGURE + + Adds additional configuration flags for + the OpenBLAS package that gets added to the make command. (see :trac:`23272`) + +.. envvar:: PARI_CONFIGURE + + Use this to pass extra parameters to PARI's ``Configure`` script, for example to specify graphics support (which is disabled by default). See the file :file:`build/pkgs/pari/spkg-install.in` for more information. -- :envvar:`SAGE_TUNE_PARI` - if yes, enable PARI self-tuning. Note that +.. envvar:: SAGE_TUNE_PARI + + If yes, enable PARI self-tuning. Note that this can be time-consuming. If you set this variable to "yes", you will also see this: ``WARNING: Tuning PARI/GP is unreliable. You may find your build of PARI fails, or PARI/GP does not work properly once built. We recommend to build this package with SAGE_CHECK="yes".`` -- :envvar:`PARI_MAKEFLAGS` - The value of this variable is passed as an +.. envvar:: PARI_MAKEFLAGS + + The value of this variable is passed as an argument to the ``$MAKE`` command when compiling PARI. -Some standard environment variables which are used by Sage: +Standard environment controlling the build process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- :envvar:`CC` - while some programs allow you to use this to specify your C +.. envvar:: CC + + While some programs allow you to use this to specify your C compiler, **not every Sage package recognizes this**. If GCC is installed within Sage, :envvar:`CC` is ignored and Sage's ``gcc`` is used instead. -- :envvar:`CPP` - similarly, this will set the C preprocessor for some Sage +.. envvar:: CPP + + Similarly, this will set the C preprocessor for some Sage packages, and similarly, using it is likely quite risky. If GCC is installed within Sage, :envvar:`CPP` is ignored and Sage's ``cpp`` is used instead. -- :envvar:`CXX` - similarly, this will set the C++ compiler for some Sage +.. envvar:: CXX + + Similarly, this will set the C++ compiler for some Sage packages, and similarly, using it is likely quite risky. If GCC is installed within Sage, :envvar:`CXX` is ignored and Sage's ``g++`` is used instead. -- :envvar:`FC` - similarly, this will set the Fortran compiler. +.. envvar:: FC + + Similarly, this will set the Fortran compiler. This is supported by all Sage packages which have Fortran code. However, for historical reasons, the value is hardcoded during the initial ``make`` and subsequent changes to ``$FC`` might be ignored (in which case, @@ -971,38 +1026,54 @@ Some standard environment variables which are used by Sage: If GCC is installed within Sage, :envvar:`FC` is ignored and Sage's ``gfortran`` is used instead. -- :envvar:`CFLAGS`, :envvar:`CXXFLAGS` and :envvar:`FCFLAGS` - the flags for +.. envvar:: CFLAGS +.. envvar:: CXXFLAGS +.. envvar:: FCFLAGS + + The flags for the C compiler, the C++ compiler and the Fortran compiler, respectively. The same comments apply to these: setting them may cause problems, because they are not universally respected among the Sage packages. Note also that ``export CFLAGS=""`` does not have the same effect as ``unset CFLAGS``. The latter is preferable. -- Similar comments apply to other compiler and linker flags like - :envvar:`CPPFLAGS`, :envvar:`LDFLAGS`, :envvar:`CXXFLAG64`, - :envvar:`LDFLAG64`, and :envvar:`LD`. +.. envvar:: CPPFLAGS +.. envvar:: LDFLAGS +.. envvar:: CXXFLAG64 +.. envvar:: LDFLAG64 +.. envvar:: LD -- :envvar:`OPENBLAS_CONFIGURE` - adds additional configuration flags for - the OpenBLAS package that gets added to the make command. (see :trac:`23272`) + Similar comments apply to these compiler and linker flags. -Environment variables dealing with doctesting: +Environment variables dealing with doctesting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- :envvar:`SAGE_TIMEOUT` - used for Sage's doctesting: the number of seconds +.. envvar:: SAGE_TIMEOUT + + used for Sage's doctesting: the number of seconds to allow a doctest before timing it out. If this isn't set, the default is 300 seconds (5 minutes). -- :envvar:`SAGE_TIMEOUT_LONG` - used for Sage's doctesting: the number of +.. envvar:: SAGE_TIMEOUT_LONG + + used for Sage's doctesting: the number of seconds to allow a doctest before timing it out, if tests are run using ``sage -t --long``. If this isn't set, the default is 1800 seconds (30 minutes). -- :envvar:`SAGE_TEST_GLOBAL_ITER`, :envvar:`SAGE_TEST_ITER` - these can +.. envvar:: SAGE_TEST_GLOBAL_ITER +.. envvar:: SAGE_TEST_ITER + + These can be used instead of passing the flags ``--global-iterations`` and ``--file-iterations``, respectively, to ``sage -t``. Indeed, these variables are only used if the flags are unset. Run ``sage -t -h`` for more information on the effects of these flags (and therefore these variables). +Environment variables set within Sage environments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Sage sets some other environment variables. The most accurate way to see what Sage does is to first run ``env`` from a shell prompt to see what environment variables you have set. Then run ``sage --sh -c From f2e968407c3f5696e921f19b83a66e0505f06ead Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jan 2024 17:13:48 -0800 Subject: [PATCH 015/191] src/doc/en/installation/source.rst: Move standard envvars earlier --- src/doc/en/installation/source.rst | 167 +++++++++++++++-------------- 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index c3052e5c5f8..ee62cbb3934 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -645,9 +645,6 @@ how it is built: Environment variables --------------------- -Environment variables controlling the build process -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Sage uses several environment variables to control its build process. Most users won't need to set any of these: the build process just works on many platforms. @@ -656,20 +653,9 @@ speed up the process.) Building Sage involves building about 100 packages, each of which has its own compilation instructions. -The Sage source tarball already includes the sources for all standard -packages, that is, it allows you to build Sage without internet -connection. The git repository, however, does not contain the source -code for third-party packages. Instead, it will be downloaded as -needed (Note: you can run ``make download`` to force downloading -packages before building). Package downloads use the Sage mirror -network, the nearest mirror will be determined automatically for -you. This is influenced by the following environment variable: - -.. envvar:: SAGE_SERVER - Try the specified mirror first, before falling back to the official - Sage mirror list. Note that Sage will search the directory - ``SAGE_SERVER/spkg/upstream`` for upstream tarballs. +Standard environment controlling the build process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here are some of the more commonly used variables affecting the build process: @@ -696,6 +682,86 @@ Here are some of the more commonly used variables affecting the build process: Some users on single-core macOS machines have reported problems when building Sage with ``MAKE='make -jNUM'`` with ``NUM`` greater than one. +.. envvar:: V + + If set to ``0``, silence the build. Instead of showing a detailed + compilation log, only one line of output is shown at the beginning + and at the end of the installation of each Sage package. To see + even less output, use:: + + $ make -s V=0 + + (Note that the above uses the syntax of setting a Makefile variable.) + +.. envvar:: CC + + While some programs allow you to use this to specify your C + compiler, **not every Sage package recognizes this**. + If GCC is installed within Sage, :envvar:`CC` is ignored and Sage's ``gcc`` + is used instead. + +.. envvar:: CPP + + Similarly, this will set the C preprocessor for some Sage + packages, and similarly, using it is likely quite risky. + If GCC is installed within Sage, :envvar:`CPP` is ignored and Sage's ``cpp`` + is used instead. + +.. envvar:: CXX + + Similarly, this will set the C++ compiler for some Sage + packages, and similarly, using it is likely quite risky. + If GCC is installed within Sage, :envvar:`CXX` is ignored and Sage's ``g++`` + is used instead. + +.. envvar:: FC + + Similarly, this will set the Fortran compiler. + This is supported by all Sage packages which have Fortran code. + However, for historical reasons, the value is hardcoded during the initial + ``make`` and subsequent changes to ``$FC`` might be ignored (in which case, + the original value will be used instead). + If GCC is installed within Sage, :envvar:`FC` is ignored and Sage's + ``gfortran`` is used instead. + +.. envvar:: CFLAGS +.. envvar:: CXXFLAGS +.. envvar:: FCFLAGS + + The flags for + the C compiler, the C++ compiler and the Fortran compiler, respectively. + The same comments apply to these: setting them may cause problems, because + they are not universally respected among the Sage packages. Note + also that ``export CFLAGS=""`` does not have the same effect as + ``unset CFLAGS``. The latter is preferable. + +.. envvar:: CPPFLAGS +.. envvar:: LDFLAGS +.. envvar:: CXXFLAG64 +.. envvar:: LDFLAG64 +.. envvar:: LD + + Similar comments apply to these compiler and linker flags. + + +Sage-specific environment variables controlling the build process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. envvar:: SAGE_SERVER + + The Sage source tarball already includes the sources for all standard + packages, that is, it allows you to build Sage without internet + connection. The git repository, however, does not contain the source + code for third-party packages. Instead, it will be downloaded as + needed (note: you can run ``make download`` to force downloading + packages before building). Package downloads use the Sage mirror + network, the nearest mirror will be determined automatically for + you. This is influenced by the following environment variable: + + Try the specified mirror first, before falling back to the official + Sage mirror list. Note that Sage will search the directory + ``SAGE_SERVER/spkg/upstream`` for upstream tarballs. + .. envvar:: SAGE_NUM_THREADS If set to a number, then when rebuilding with ``sage -b`` or @@ -714,17 +780,6 @@ Here are some of the more commonly used variables affecting the build process: When ``sage -t -p`` runs under the control of the GNU ``make`` jobserver, then Sage will request as most this number of job slots. -.. envvar:: V - - If set to ``0``, silence the build. Instead of showing a detailed - compilation log, only one line of output is shown at the beginning - and at the end of the installation of each Sage package. To see - even less output, use:: - - $ make -s V=0 - - (Note that the above uses the syntax of setting a Makefile variable.) - .. envvar:: SAGE_CHECK If set to ``yes``, then during the build process, @@ -886,8 +941,9 @@ Here are some of the more commonly used variables affecting the build process: supports :envvar:`SAGE_SUDO`, into a root-owned installation hierarchy (:envvar:`SAGE_LOCAL`). -Environment variables for documentation build -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Environment variables controlling the documentation build +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. envvar:: SAGE_DOCBUILD_OPTS @@ -992,58 +1048,6 @@ Environment variables dealing with specific Sage packages The value of this variable is passed as an argument to the ``$MAKE`` command when compiling PARI. -Standard environment controlling the build process -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. envvar:: CC - - While some programs allow you to use this to specify your C - compiler, **not every Sage package recognizes this**. - If GCC is installed within Sage, :envvar:`CC` is ignored and Sage's ``gcc`` - is used instead. - -.. envvar:: CPP - - Similarly, this will set the C preprocessor for some Sage - packages, and similarly, using it is likely quite risky. - If GCC is installed within Sage, :envvar:`CPP` is ignored and Sage's ``cpp`` - is used instead. - -.. envvar:: CXX - - Similarly, this will set the C++ compiler for some Sage - packages, and similarly, using it is likely quite risky. - If GCC is installed within Sage, :envvar:`CXX` is ignored and Sage's ``g++`` - is used instead. - -.. envvar:: FC - - Similarly, this will set the Fortran compiler. - This is supported by all Sage packages which have Fortran code. - However, for historical reasons, the value is hardcoded during the initial - ``make`` and subsequent changes to ``$FC`` might be ignored (in which case, - the original value will be used instead). - If GCC is installed within Sage, :envvar:`FC` is ignored and Sage's - ``gfortran`` is used instead. - -.. envvar:: CFLAGS -.. envvar:: CXXFLAGS -.. envvar:: FCFLAGS - - The flags for - the C compiler, the C++ compiler and the Fortran compiler, respectively. - The same comments apply to these: setting them may cause problems, because - they are not universally respected among the Sage packages. Note - also that ``export CFLAGS=""`` does not have the same effect as - ``unset CFLAGS``. The latter is preferable. - -.. envvar:: CPPFLAGS -.. envvar:: LDFLAGS -.. envvar:: CXXFLAG64 -.. envvar:: LDFLAG64 -.. envvar:: LD - - Similar comments apply to these compiler and linker flags. Environment variables dealing with doctesting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1071,6 +1075,7 @@ Environment variables dealing with doctesting for more information on the effects of these flags (and therefore these variables). + Environment variables set within Sage environments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d52c66deedf47da572cdb12097eddd22d1b34edb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jan 2024 17:29:54 -0800 Subject: [PATCH 016/191] src/doc/en/installation/source.rst: Updates on some envvars --- src/doc/en/installation/source.rst | 31 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index ee62cbb3934..5591f8809b5 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -650,7 +650,7 @@ Most users won't need to set any of these: the build process just works on many platforms. (Note though that setting :envvar:`MAKE`, as described below, can significantly speed up the process.) -Building Sage involves building about 100 packages, each of which has its own +Building Sage involves building many packages, each of which has its own compilation instructions. @@ -677,11 +677,6 @@ Here are some of the more commonly used variables affecting the build process: and `Parallel building `_. - .. warning:: - - Some users on single-core macOS machines have reported problems when - building Sage with ``MAKE='make -jNUM'`` with ``NUM`` greater than one. - .. envvar:: V If set to ``0``, silence the build. Instead of showing a detailed @@ -754,14 +749,22 @@ Sage-specific environment variables controlling the build process connection. The git repository, however, does not contain the source code for third-party packages. Instead, it will be downloaded as needed (note: you can run ``make download`` to force downloading - packages before building). Package downloads use the Sage mirror - network, the nearest mirror will be determined automatically for - you. This is influenced by the following environment variable: + packages before building). - Try the specified mirror first, before falling back to the official - Sage mirror list. Note that Sage will search the directory + If :envvar:`SAGE_SERVER` is set, the specified Sage mirror is contacted + first. Note that Sage will search the directory ``SAGE_SERVER/spkg/upstream`` for upstream tarballs. + If downloading a file from there fails or :envvar:`SAGE_SERVER` is not set, + files will be attempted to download from release assets of the + Sage GitHub repository. + + If that fails too, the Sage mirror network is contacted to determine + the nearest mirrors. + + This sequence of operations is defined by the files in the directory + :file:`$SAGE_ROOT/.upstream.d`. + .. envvar:: SAGE_NUM_THREADS If set to a number, then when rebuilding with ``sage -b`` or @@ -1025,7 +1028,7 @@ Environment variables dealing with specific Sage packages .. envvar:: OPENBLAS_CONFIGURE Adds additional configuration flags for - the OpenBLAS package that gets added to the make command. (see :trac:`23272`) + the OpenBLAS package that gets added to the ``make`` command. (see :trac:`23272`) .. envvar:: PARI_CONFIGURE @@ -1054,13 +1057,13 @@ Environment variables dealing with doctesting .. envvar:: SAGE_TIMEOUT - used for Sage's doctesting: the number of seconds + Used for Sage's doctesting: the number of seconds to allow a doctest before timing it out. If this isn't set, the default is 300 seconds (5 minutes). .. envvar:: SAGE_TIMEOUT_LONG - used for Sage's doctesting: the number of + Used for Sage's doctesting: the number of seconds to allow a doctest before timing it out, if tests are run using ``sage -t --long``. If this isn't set, the default is 1800 seconds (30 minutes). From 2fe08b6d8f7016d8cc55b91b77d517a2ad77ca67 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 11:57:14 +0100 Subject: [PATCH 017/191] Fix #6413 Compute pth multiplication coordinate maps by using isogenies. Thanks @yyyyx4 for the idea! --- .../schemes/elliptic_curves/ell_generic.py | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 57003863b1b..3fd4df3961a 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -53,6 +53,7 @@ # **************************************************************************** import math +from sage.arith.misc import valuation import sage.rings.abc from sage.rings.finite_rings.integer_mod import mod @@ -65,6 +66,7 @@ import sage.groups.generic as generic from sage.arith.functions import lcm +from sage.functions.generalized import sgn from sage.rings.integer import Integer from sage.rings.big_oh import O from sage.rings.infinity import Infinity as oo @@ -2019,6 +2021,30 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2, force_evalu torsion_polynomial = division_polynomial + @cached_method + def multiplication_by_p_isogeny(self): + r""" + Return the multiplication-by-\(p\) isogeny. + + EXAMPLES:: + + sage: p = 23 + sage: K. = GF(p^3) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: phi = E.multiplication_by_p_isogeny() + sage: assert phi.degree() == p**2 + sage: P = E.random_element() + sage: assert phi(P) == P * p + """ + from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic + + K = self.base_ring() + if not isinstance(K, FiniteField_generic): + raise ValueError(f"Base ring (={K}) is not a finite field.") + + frob = self.frobenius_isogeny() + return frob.dual() * frob + def _multiple_x_numerator(self, n, x=None): r""" Return the numerator of the `x`-coordinate of the `n\th` multiple of a @@ -2333,6 +2359,20 @@ def multiplication_by_m(self, m, x_only=False): sage: my_eval = lambda f,P: [fi(P[0],P[1]) for fi in f] sage: f = E.multiplication_by_m(2) sage: assert(E(eval(f,P)) == 2*P) + + The following test shows that :trac:`6413` is indeed fixed:: + sage: p = 7 + sage: K. = GF(p^2) + sage: E = EllipticCurve(K, [a + 3, 5 - a]) + sage: k = p^2 * 3 + sage: f, g = E.multiplication_by_m(k) + sage: for _ in range(100): + ....: P = E.random_point() + ....: if P * k == 0: + ....: continue + ....: Qx = f.subs(x=P[0]) + ....: Qy = g.subs(x=P[0], y=P[1]) + ....: assert (P * k).xy() == (Qx, Qy) """ # Coerce the input m to be an integer m = Integer(m) @@ -2352,7 +2392,7 @@ def multiplication_by_m(self, m, x_only=False): return x # Grab curve invariants - a1, a2, a3, a4, a6 = self.a_invariants() + a1, _, a3, _, _ = self.a_invariants() if m == -1: if not x_only: @@ -2360,22 +2400,43 @@ def multiplication_by_m(self, m, x_only=False): else: return x - # the x-coordinate does not depend on the sign of m. The work + # Inseparable cases + # Special case of multiplication by p is easy. Kind of. + p = Integer(self.base_ring().characteristic()) + + v_p = 0 if p == 0 else valuation(m.abs(), p) + m //= p**v_p + + # the x-coordinate does not depend on the sign of m. The work # here is done by functions defined earlier: mx = (x.parent()(self._multiple_x_numerator(m.abs(), x)) / x.parent()(self._multiple_x_denominator(m.abs(), x))) + if x_only: + # slow. + if v_p > 0: + p_endo = self.multiplication_by_p_isogeny() + isog = p_endo**v_p + fx = isog.x_rational_map() + # slow. + mx = mx.subs(x=fx) # Return it if the optional parameter x_only is set. return mx - # Consideration of the invariant differential - # w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3) - # and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx - + # Consideration of the invariant differential + # w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3) + # and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2 + if v_p > 0: + frob = self.frobenius_isogeny() + isog = (frob.dual() * frob)**v_p + fx, fy = isog.rational_maps() + # slow... + my = my.subs(x=fx, y=fy) + return mx, my def multiplication_by_m_isogeny(self, m): From 4273b70e4c9bfa39ec235be85b680f63195a28de Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 17:33:05 +0100 Subject: [PATCH 018/191] Fix --- src/sage/schemes/elliptic_curves/ell_generic.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 3fd4df3961a..6a0a11bf1f4 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2400,12 +2400,12 @@ def multiplication_by_m(self, m, x_only=False): else: return x - # Inseparable cases - # Special case of multiplication by p is easy. Kind of. - p = Integer(self.base_ring().characteristic()) - - v_p = 0 if p == 0 else valuation(m.abs(), p) - m //= p**v_p + # If we only require the x coordinate, it is faster to use the recursive formula + # since substituting polynomials is quite slow. + if not x_only: + p = Integer(self.base_ring().characteristic()) + v_p = 0 if p == 0 else valuation(m.abs(), p) + m //= p**v_p # the x-coordinate does not depend on the sign of m. The work # here is done by functions defined earlier: From 8ab10810a1181ee72f4904792b758096cf0baf10 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 17:32:18 +0100 Subject: [PATCH 019/191] Remove useless imports --- src/sage/schemes/elliptic_curves/ell_generic.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 6a0a11bf1f4..de0c2f67a67 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -66,10 +66,7 @@ import sage.groups.generic as generic from sage.arith.functions import lcm -from sage.functions.generalized import sgn from sage.rings.integer import Integer -from sage.rings.big_oh import O -from sage.rings.infinity import Infinity as oo from sage.rings.rational import Rational from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.rational_field import RationalField From 6a81d72d927e91c10b4ff60f9a42bfb0c45a7b6b Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 17:46:23 +0100 Subject: [PATCH 020/191] serious fix (one character) --- src/sage/schemes/elliptic_curves/ell_generic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index de0c2f67a67..f7eaa417da3 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2410,7 +2410,6 @@ def multiplication_by_m(self, m, x_only=False): mx = (x.parent()(self._multiple_x_numerator(m.abs(), x)) / x.parent()(self._multiple_x_denominator(m.abs(), x))) - if x_only: # slow. if v_p > 0: From 6145cff42c9705566c08949356bb08109f073cba Mon Sep 17 00:00:00 2001 From: syndrakon Date: Fri, 19 Jan 2024 15:12:43 +0100 Subject: [PATCH 021/191] Define and document behaviour of < and > for fractional ideals in a quaternion algebra over QQ "smaller than" now means "included in" --- .../algebras/quatalg/quaternion_algebra.py | 29 ++++++++++++++++++- src/sage/modular/quatalg/brandt.py | 3 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 5a615b43978..50b7a2448ce 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -2393,8 +2393,35 @@ def _richcmp_(self, right, op): sage: I != I # indirect doctest False + + TESTS: + sage: B = QuaternionAlgebra(QQ,-1,-11) + sage: i,j,k = B.gens() + sage: I = B.ideal([1,i,j,i*j]) + sage: I == I + True + sage: O = B.ideal([1,i,(i+j)/2,(1+i*j)/2]) + sage: I <= O + True + sage: I >= O + False + sage: I != O + True + sage: I == O + False + sage: I != I + False + sage: I < I + False + sage: I < O + True + sage: I <= I + True + sage: O >= O + True + """ - return self.basis_matrix()._richcmp_(right.basis_matrix(), op) + return self.free_module().__richcmp__(right.free_module(), op) def __hash__(self): """ diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 1c74d953f6d..277d5c0d45c 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -1365,7 +1365,8 @@ def right_ideals(self, B=None): ideals_theta[J_theta] = [J] verbose("found %s of %s ideals" % (len(ideals), self.dimension()), level=2) if len(ideals) >= self.dimension(): - ideals = tuple(sorted(ideals)) + #order by basis matrix (as ideals were previously ordered) for backward compatibility and deterministic order of the output + ideals = tuple(sorted(ideals, key=lambda x: x.basis_matrix())) self.__right_ideals = ideals return ideals got_something_new = True From f6b41f466fd18aca8a29a572cf3be744d35bd11d Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 2 Jan 2024 00:27:53 +0100 Subject: [PATCH 022/191] make sure x-coordinate lies in base ring of curve --- src/sage/schemes/elliptic_curves/ell_generic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 054ad695ef8..0ab64f86cba 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -963,6 +963,7 @@ def lift_x(self, x, all=False, extend=False): else: ys = [y1, y2] ys.sort() # ensure deterministic behavior + x = M(x) one = M.one() if all: return [EM.point([x, y, one], check=False) for y in ys] From 643729ef0a5a2a9032cd3c6f84eb4ef27844f32d Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 2 Jan 2024 00:26:15 +0100 Subject: [PATCH 023/191] deduplicate check=True code --- src/sage/schemes/elliptic_curves/ell_point.py | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index e86d0c8a5cf..3fc26e7e230 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -273,42 +273,8 @@ def __init__(self, curve, v, check=True): v = list(v) elif v == 0: v = (R.zero(), R.one(), R.zero()) - if check: - # mostly from SchemeMorphism_point_projective_field - d = point_homset.codomain().ambient_space().ngens() - if not isinstance(v, (list, tuple)): - raise TypeError("Argument v (= %s) must be a scheme point, list, or tuple." % str(v)) - if len(v) != d and len(v) != d-1: - raise TypeError("v (=%s) must have %s components" % (v, d)) - v = Sequence(v, R) - if len(v) == d-1: # very common special case - v.append(v.universe()(1)) - - n = len(v) - all_zero = True - for i in range(n): - c = v[n-1-i] - if c: - all_zero = False - if c == 1: - break - for j in range(n-i): - v[j] /= c - break - if all_zero: - raise ValueError("%s does not define a valid point " - "since all entries are 0" % repr(v)) - - x, y, z = v - if z == 0: - test = x - else: - a1, a2, a3, a4, a6 = curve.ainvs() - test = y**2 + (a1*x+a3)*y - (((x+a2)*x+a4)*x+a6) - if not test == 0: - raise TypeError("Coordinates %s do not define a point on %s" % (list(v), curve)) - SchemeMorphism_point_abelian_variety_field.__init__(self, point_homset, v, check=False) + SchemeMorphism_point_abelian_variety_field.__init__(self, point_homset, v, check=check) # AdditiveGroupElement.__init__(self, point_homset) def _repr_(self): From ac0ce58d0f2ea6b7aab6f5cdeb3f3a0ce0fc5821 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 22 Jan 2024 01:54:12 +0000 Subject: [PATCH 024/191] Remove deprecated `multiplication_by_m_isogeny` --- .../schemes/elliptic_curves/ell_generic.py | 95 ------------------- src/sage/schemes/elliptic_curves/hom.py | 15 --- .../schemes/elliptic_curves/hom_scalar.py | 3 +- 3 files changed, 1 insertion(+), 112 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index f7eaa417da3..ea3d8992e46 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2435,101 +2435,6 @@ def multiplication_by_m(self, m, x_only=False): return mx, my - def multiplication_by_m_isogeny(self, m): - r""" - Return the ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on this elliptic curve. - - The resulting isogeny will - have the associated rational maps (i.e., those returned by - :meth:`multiplication_by_m`) already computed. - - NOTE: This function is currently *much* slower than the - result of ``self.multiplication_by_m()``, because - constructing an isogeny precomputes a significant amount - of information. See :trac:`7368` and :trac:`8014` for the - status of improving this situation. - - INPUT: - - - ``m`` -- a nonzero integer - - OUTPUT: - - - An ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on this elliptic curve. - - EXAMPLES:: - - sage: E = EllipticCurve('11a1') - sage: E.multiplication_by_m_isogeny(7) - doctest:warning ... DeprecationWarning: ... - Isogeny of degree 49 - from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 - over Rational Field - to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 - over Rational Field - - TESTS: - - Tests for :trac:`32490`:: - - sage: E = EllipticCurve(QQbar, [1,0]) # needs sage.rings.number_field - sage: E.multiplication_by_m_isogeny(1).rational_maps() # needs sage.rings.number_field - (x, y) - - :: - - sage: E = EllipticCurve_from_j(GF(31337).random_element()) # needs sage.rings.finite_rings - sage: P = E.random_point() # needs sage.rings.finite_rings - sage: [E.multiplication_by_m_isogeny(m)(P) == m*P for m in (1,2,3,5,7,9)] # needs sage.rings.finite_rings - [True, True, True, True, True, True] - - :: - - sage: E = EllipticCurve('99.a1') - sage: E.multiplication_by_m_isogeny(5) - Isogeny of degree 25 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field - sage: E.multiplication_by_m_isogeny(2).rational_maps() - ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), - (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) - - Test for :trac:`34727`:: - - sage: E = EllipticCurve([5,5]) - sage: E.multiplication_by_m_isogeny(-1) - Isogeny of degree 1 - from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - sage: E.multiplication_by_m_isogeny(-2) - Isogeny of degree 4 - from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - sage: E.multiplication_by_m_isogeny(-3) - Isogeny of degree 9 - from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - sage: mu = E.multiplication_by_m_isogeny - sage: all(mu(-m) == -mu(m) for m in (1,2,3,5,7)) - True - """ - from sage.misc.superseded import deprecation - deprecation(32826, 'The .multiplication_by_m_isogeny() method is superseded by .scalar_multiplication().') - - mx, my = self.multiplication_by_m(m) - - torsion_poly = self.torsion_polynomial(abs(m)).monic() - phi = self.isogeny(torsion_poly, codomain=self) - phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) - - # trac 32490: using codomain=self can give a wrong isomorphism - for aut in self.automorphisms(): - psi = aut * phi - if psi.rational_maps() == (mx, my): - return psi - - assert False, 'bug in multiplication_by_m_isogeny()' - def scalar_multiplication(self, m): r""" Return the scalar-multiplication map `[m]` on this elliptic diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index fb7855993ea..627d9099237 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -227,21 +227,6 @@ def _richcmp_(self, other, op): sage: phi.dual() == psi.dual() True - :: - - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism - sage: E = EllipticCurve([9,9]) - sage: F = E.change_ring(GF(71)) - sage: wE = identity_morphism(E) - sage: wF = identity_morphism(F) - sage: mE = E.scalar_multiplication(1) - sage: mF = F.multiplication_by_m_isogeny(1) - doctest:warning ... DeprecationWarning: ... - sage: [mE == wE, mF == wF] - [True, True] - sage: [a == b for a in (wE,mE) for b in (wF,mF)] - [False, False, False, False] - .. SEEALSO:: - :meth:`_comparison_impl` diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index 03f042fd830..37994a864d0 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -381,8 +381,7 @@ def scaling_factor(self): sage: u = phi.scaling_factor() sage: u == phi.formal()[1] True - sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() - doctest:warning ... DeprecationWarning: ... + sage: u == 5 True The scaling factor lives in the base ring:: From 94fc8710e4e82207648b3d0f99a1032f974a7903 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 22 Jan 2024 04:48:44 +0000 Subject: [PATCH 025/191] Fix unbounded local variable bug --- src/sage/schemes/elliptic_curves/ell_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index ea3d8992e46..fc8861b373c 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2399,9 +2399,9 @@ def multiplication_by_m(self, m, x_only=False): # If we only require the x coordinate, it is faster to use the recursive formula # since substituting polynomials is quite slow. + p = Integer(self.base_ring().characteristic()) + v_p = 0 if p == 0 else valuation(m.abs(), p) if not x_only: - p = Integer(self.base_ring().characteristic()) - v_p = 0 if p == 0 else valuation(m.abs(), p) m //= p**v_p # the x-coordinate does not depend on the sign of m. The work From 498bd371c0bee2483db6e74becd36b152bfe46b2 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 24 Aug 2022 09:40:18 +0800 Subject: [PATCH 026/191] normalize projective points over (quotients of) Euclidean domains --- .../arithmetic_dynamics/projective_ds.py | 6 +- .../schemes/elliptic_curves/constructor.py | 2 +- src/sage/schemes/elliptic_curves/ell_point.py | 11 ++- .../schemes/projective/projective_point.py | 93 ++++++++++--------- 4 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 0242f456959..740d25358d8 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -1081,8 +1081,7 @@ def nth_iterate(self, P, n, **kwds): sage: f.nth_iterate(P(0, 1), 3) Traceback (most recent call last): ... - ValueError: [0, 0] does not define a point in Projective Space of - dimension 1 over Rational Field since all entries are zero + ValueError: [0, 0] does not define a valid projective point since all entries are zero :: @@ -1597,8 +1596,7 @@ def orbit(self, P, N, **kwds): sage: f.orbit(P(0, 1), 3) Traceback (most recent call last): ... - ValueError: [0, 0] does not define a point in Projective Space of - dimension 1 over Rational Field since all entries are zero + ValueError: [0, 0] does not define a valid projective point since all entries are zero sage: f.orbit(P(0, 1), 3, check=False) [(0 : 1), (0 : 0), (0 : 0), (0 : 0)] diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index c8afc298230..c08e3cc5d4a 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -921,7 +921,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: f([1,-1,1]) Traceback (most recent call last): ... - ValueError: [0, 0, 0] does not define a valid point since all entries are 0 + ValueError: [0, 0, 0] does not define a valid projective point since all entries are zero Using the group law on the codomain elliptic curve, which has rank 1 and full 2-torsion, and the inverse morphism, we can find many diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 3fc26e7e230..350700b9e0e 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -277,6 +277,8 @@ def __init__(self, curve, v, check=True): SchemeMorphism_point_abelian_variety_field.__init__(self, point_homset, v, check=check) # AdditiveGroupElement.__init__(self, point_homset) + self.normalize_coordinates() + def _repr_(self): """ Return a string representation of this point. @@ -398,8 +400,9 @@ def __pari__(self): sage: pari(E).elladd(O, P) [Mod(1, 11), Mod(2, 11)] """ - if self[2]: - return pari([self[0]/self[2], self[1]/self[2]]) + x,y,z = self._coords + if z: + return pari([x/z, y/z]) else: return pari([0]) @@ -504,7 +507,7 @@ def __bool__(self): sage: P.is_zero() False """ - return bool(self[2]) + return bool(self._coords[2]) def has_order(self, n): r""" @@ -838,7 +841,7 @@ def xy(self): ... ZeroDivisionError: rational division by zero """ - if self[2] == 1: + if self[2].is_one(): return self[0], self[1] else: return self[0]/self[2], self[1]/self[2] diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 55bd5354976..dea15db15e5 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -105,8 +105,7 @@ def __init__(self, X, v, check=True): sage: P(0,0,0,0) Traceback (most recent call last): ... - ValueError: [0, 0, 0, 0] does not define a point in Projective Space of dimension 3 - over Integer Ring since all entries are zero + ValueError: [0, 0, 0, 0] does not define a valid projective point since all entries are zero :: @@ -120,8 +119,7 @@ def __init__(self, X, v, check=True): sage: P(0,5,10,15) Traceback (most recent call last): ... - ValueError: [0, 5, 10, 0] does not define a point in Projective Space of dimension 3 - over Ring of integers modulo 15 since it is a multiple of a zero divisor + ValueError: [0, 5, 10, 0] does not define a valid projective point since it is a multiple of a zero divisor It is possible to avoid the possibly time-consuming checks, but be careful!! :: @@ -164,6 +162,7 @@ def __init__(self, X, v, check=True): ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) + if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field from sage.rings.ring import CommutativeRing @@ -190,21 +189,20 @@ def __init__(self, X, v, check=True): # Over integral domains, any tuple with at least one # non-zero coordinate is a valid projective point. if not any(v): - raise ValueError(f"{v} does not define a point " - f"in {X.codomain()} " - "since all entries are zero") + raise ValueError(f"{v} does not define a valid projective " + "point since all entries are zero") else: # Over rings with zero divisors, a more careful check # is required: We test whether the coordinates of the # point generate the unit ideal. See #31576. if 1 not in R.ideal(v): - raise ValueError(f"{v} does not define a point " - f"in {X.codomain()} " + raise ValueError(f"{v} does not define a valid projective point " "since it is a multiple of a zero divisor") X.extended_codomain()._check_satisfies_equations(v) self._coords = tuple(v) + self._normalized = False def _richcmp_(self, right, op): """ @@ -531,11 +529,12 @@ def scale_by(self, t): R = self.codomain().base_ring() if isinstance(R, QuotientRing_generic): for i in range(self.codomain().ambient_space().dimension_relative()+1): - new_coords = [R(u.lift()*t) for u in self] + new_coords = [R(u.lift()*t) for u in self._coords] else: for i in range(self.codomain().ambient_space().dimension_relative()+1): - new_coords = [R(u*t) for u in self] + new_coords = [R(u*t) for u in self._coords] self._coords = tuple(new_coords) + self._normalized = False def normalize_coordinates(self): """ @@ -596,7 +595,7 @@ def normalize_coordinates(self): :: sage: # needs sage.libs.singular - sage: R. = PolynomialRing(QQ, 1) + sage: R. = QQ[] sage: S = R.quotient_ring(R.ideal(t^3)) sage: P. = ProjectiveSpace(S, 1) sage: Q = P(t + 1, t^2 + t) @@ -604,35 +603,32 @@ def normalize_coordinates(self): sage: Q (1 : tbar) """ + if self._normalized: + return R = self.codomain().base_ring() - if isinstance(R,(QuotientRing_generic)): - GCD = gcd(self[0].lift(),self[1].lift()) - index = 2 - if self[0].lift() > 0 or self[1].lift() > 0: - neg = 1 - else: - neg = -1 - while GCD != 1 and index < len(self._coords): - if self[index].lift() > 0: - neg = 1 - GCD = gcd(GCD,self[index].lift()) - index += 1 + if isinstance(R, QuotientRing_generic): + index = self.codomain().ambient_space().dimension_relative() + while not self._coords[index]: + index -= 1 + last = self._coords[index].lift() + mod, = R.defining_ideal().gens() + unit = last + while not (zdiv := mod.gcd(unit)).is_unit(): + unit //= zdiv + self.scale_by(unit.inverse_mod(mod)) else: - GCD = R(gcd(self[0], self[1])) + GCD = R(gcd(self._coords[0], self._coords[1])) index = 2 - if self[0] > 0 or self[1] > 0: - neg = R(1) - else: - neg = R(-1) + neg = self._coords[0] <= 0 and self._coords[1] <= 0 while GCD != 1 and index < len(self._coords): - if self[index] > 0: - neg = R(1) - GCD = R(gcd(GCD,self[index])) + neg = self._coords[index] <= 0 + GCD = R(gcd(GCD, self._coords[index])) index += 1 - if GCD != 1: - self.scale_by(neg/GCD) - elif neg == -1: - self.scale_by(neg) + if GCD != 1: + self.scale_by(~GCD) + if neg: + self.scale_by(-1) + self._normalized = True def dehomogenize(self,n): r""" @@ -1103,8 +1099,7 @@ def __init__(self, X, v, check=True): sage: P(0, 0, 0, 0) Traceback (most recent call last): ... - ValueError: [0, 0, 0, 0] does not define a point in Projective Space of dimension 3 - over Rational Field since all entries are zero + ValueError: [0, 0, 0, 0] does not define a valid projective point since all entries are zero :: @@ -1141,6 +1136,9 @@ def __init__(self, X, v, check=True): ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) + + self._normalized = False + if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field from sage.rings.ring import CommutativeRing @@ -1175,11 +1173,11 @@ def __init__(self, X, v, check=True): for j in range(last): v[j] /= c v[last] = R.one() + self._normalized = True break if all_zero: - raise ValueError(f"{v} does not define a point " - f"in {X.codomain()} " - "since all entries are zero") + raise ValueError(f"{v} does not define a valid projective " + "point since all entries are zero") X.extended_codomain()._check_satisfies_equations(v) @@ -1224,10 +1222,19 @@ def normalize_coordinates(self): sage: Q.normalize_coordinates(); Q (1/2 : 1/2 : 1) """ + if self._normalized: + return index = self.codomain().ambient_space().dimension_relative() - while self[index] == 0: + while not self._coords[index]: index -= 1 - self.scale_by(1/self[index]) + inv = self._coords[index].inverse() + new_coords = [] + for i in range(index): + new_coords.append(self._coords[i] * inv) + new_coords.append(self.base_ring().one()) + new_coords.extend(self._coords[index+1:]) + self._coords = tuple(new_coords) + self._normalized = True def _number_field_from_algebraics(self): r""" From a1d9a52c750ba20ddfd78f596d7a1ebbfce3ff44 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 22 Jan 2024 16:06:40 +0100 Subject: [PATCH 027/191] specialised subs for FractionFieldElement --- src/sage/algebras/fusion_rings/fusion_ring.py | 4 +- src/sage/rings/fraction_field_element.pyx | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 72bc0f8600c..91cec5ad5c7 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1572,9 +1572,9 @@ def q_dimension(self, base_coercion=True): elif exp < 0: expr /= q_int(P._nf * val, q)**(-exp) expr = R(expr) - expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) + expr = expr.subs(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) - ret = expr.substitute(q=zet) + ret = expr.subs(q=zet) if (not base_coercion) or (self.parent()._basecoer is None): return ret diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 5b6cfa2e29f..fea008d7d13 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -446,6 +446,47 @@ cdef class FractionFieldElement(FieldElement): """ return self._numerator(*x, **kwds) / self._denominator(*x, **kwds) + def subs(self, *args, **kwds): + """ + Substitute variables in numerator and denominator. + + All the arguments are transmitted unchanged to the method + ``subs`` of the numerator and the denominator. + + INPUT: + + - ``args`` - arguments to be passed to the numerator and + denominator + - ``kwargs`` - keyword arguments to be passed to the + numerator and denominator + + OUTPUT: + + - new object if substitution is possible, otherwise self. + + EXAMPLES:: + + sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() + sage: f = x^2 + y + x^2*y^2 + 5 + sage: (1/f).subs(x=5) + 1/(25*y^2 + y + 30) + + TESTS: + + Check that :issue:`37122` is fixed:: + + sage: P = PolynomialRing(QQ, ["x%s" % i for i in range(10000)]) + sage: PF = P.fraction_field() + sage: p = sum(i*P.gen(i) for i in range(5)) / sum(i*P.gen(i) for i in range(8)) + sage: v = P.gen(4) + sage: p.subs({v: 100}) + (x1 + 2*x2 + 3*x3 + 400)/(x1 + 2*x2 + 3*x3 + 5*x5 + 6*x6 + 7*x7 + 400) + + """ + num = self._numerator.subs(*args, **kwds) + den = self._denominator.subs(*args, **kwds) + return num / den + def _is_atomic(self): """ EXAMPLES:: From 826ec07905d3e0827bdfba4875a04a2f36662c54 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 24 Jan 2024 02:25:18 +0000 Subject: [PATCH 028/191] =?UTF-8?q?apply=20review=20changes=20=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../elliptic_curves/ell_finite_field.py | 18 +++++ .../schemes/elliptic_curves/ell_generic.py | 65 ++++++++----------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 8d3eca74214..1a3e92aabd8 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -610,6 +610,24 @@ def cardinality(self, algorithm=None, extension_degree=1): order = cardinality # alias + @cached_method + def multiplication_by_p_isogeny(self): + r""" + Return the multiplication-by-\(p\) isogeny. + + EXAMPLES:: + + sage: p = 23 + sage: K. = GF(p^3) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: phi = E.multiplication_by_p_isogeny() + sage: assert phi.degree() == p**2 + sage: P = E.random_element() + sage: assert phi(P) == P * p + """ + frob = self.frobenius_isogeny() + return frob.dual() * frob + def frobenius_polynomial(self): r""" Return the characteristic polynomial of Frobenius. diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index c22e0b59bab..8caa78557ea 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2106,30 +2106,6 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2, force_evalu torsion_polynomial = division_polynomial - @cached_method - def multiplication_by_p_isogeny(self): - r""" - Return the multiplication-by-\(p\) isogeny. - - EXAMPLES:: - - sage: p = 23 - sage: K. = GF(p^3) - sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) - sage: phi = E.multiplication_by_p_isogeny() - sage: assert phi.degree() == p**2 - sage: P = E.random_element() - sage: assert phi(P) == P * p - """ - from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic - - K = self.base_ring() - if not isinstance(K, FiniteField_generic): - raise ValueError(f"Base ring (={K}) is not a finite field.") - - frob = self.frobenius_isogeny() - return frob.dual() * frob - def _multiple_x_numerator(self, n, x=None): r""" Return the numerator of the `x`-coordinate of the `n\th` multiple of a @@ -2445,7 +2421,7 @@ def multiplication_by_m(self, m, x_only=False): sage: f = E.multiplication_by_m(2) sage: assert(E(eval(f,P)) == 2*P) - The following test shows that :trac:`6413` is indeed fixed:: + The following test shows that :trac:`6413` is fixed for elliptic curves over finite fields:: sage: p = 7 sage: K. = GF(p^2) sage: E = EllipticCurve(K, [a + 3, 5 - a]) @@ -2458,15 +2434,39 @@ def multiplication_by_m(self, m, x_only=False): ....: Qx = f.subs(x=P[0]) ....: Qy = g.subs(x=P[0], y=P[1]) ....: assert (P * k).xy() == (Qx, Qy) + + However, it still fails for elliptic curves over positive characteristics fields:: + + sage: F. = FunctionField(GF(7)) + sage: E = EllipticCurve(F, [a, 1 / a]) + sage: E.multiplication_by_m(7) + Traceback (most recent call last): + ... + NotImplementedError: multiplication by integer not coprime to pis only implemented for curves over finite fields + + :: + + sage: p = 7 + sage: K. = GF(p^2) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: E.multiplication_by_m(p * 2)[0] == E.multiplication_by_m(p * 2, x_only=True) + True """ # Coerce the input m to be an integer m = Integer(m) + p = self.base_ring().characteristic() + if m == 0: raise ValueError("m must be a non-zero integer") if x_only: x = polygen(self.base_ring(), 'x') else: + from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic + if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): + # TODO: Implement the correct formula? + raise NotImplementedError("multiplication by integer not coprime to p" + "is only implemented for curves over finite fields") x, y = polygens(self.base_ring(), 'x,y') # Special case of multiplication by 1 is easy. @@ -2487,8 +2487,7 @@ def multiplication_by_m(self, m, x_only=False): # If we only require the x coordinate, it is faster to use the recursive formula # since substituting polynomials is quite slow. - p = Integer(self.base_ring().characteristic()) - v_p = 0 if p == 0 else valuation(m.abs(), p) + v_p = 0 if p == 0 else valuation(m, p) if not x_only: m //= p**v_p @@ -2499,14 +2498,6 @@ def multiplication_by_m(self, m, x_only=False): / x.parent()(self._multiple_x_denominator(m.abs(), x))) if x_only: - # slow. - if v_p > 0: - p_endo = self.multiplication_by_p_isogeny() - isog = p_endo**v_p - fx = isog.x_rational_map() - # slow. - mx = mx.subs(x=fx) - # Return it if the optional parameter x_only is set. return mx # Consideration of the invariant differential @@ -2515,10 +2506,10 @@ def multiplication_by_m(self, m, x_only=False): my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2 if v_p > 0: - frob = self.frobenius_isogeny() - isog = (frob.dual() * frob)**v_p + isog = self.multiplication_by_p_isogeny()**v_p fx, fy = isog.rational_maps() # slow... + mx = mx.subs(x=fx) my = my.subs(x=fx, y=fy) return mx, my From b17a2e331d3e4562e26b8096740cb81891b855fa Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 24 Jan 2024 14:21:18 +0200 Subject: [PATCH 029/191] Add CircuitsMatroid class --- src/doc/en/reference/matroids/index.rst | 1 + src/sage/matroids/circuits_matroid.pxd | 30 ++ src/sage/matroids/circuits_matroid.pyx | 645 ++++++++++++++++++++++++ src/sage/matroids/constructor.py | 42 +- src/sage/matroids/database_matroids.py | 7 +- src/sage/matroids/unpickling.pyx | 53 +- 6 files changed, 747 insertions(+), 31 deletions(-) create mode 100644 src/sage/matroids/circuits_matroid.pxd create mode 100644 src/sage/matroids/circuits_matroid.pyx diff --git a/src/doc/en/reference/matroids/index.rst b/src/doc/en/reference/matroids/index.rst index 4b854c47573..692d798d222 100644 --- a/src/doc/en/reference/matroids/index.rst +++ b/src/doc/en/reference/matroids/index.rst @@ -27,6 +27,7 @@ Concrete implementations :maxdepth: 1 sage/matroids/basis_matroid + sage/matroids/circuits_matroid sage/matroids/circuit_closures_matroid sage/matroids/linear_matroid sage/matroids/rank_matroid diff --git a/src/sage/matroids/circuits_matroid.pxd b/src/sage/matroids/circuits_matroid.pxd new file mode 100644 index 00000000000..2452e639726 --- /dev/null +++ b/src/sage/matroids/circuits_matroid.pxd @@ -0,0 +1,30 @@ +from sage.matroids.matroid cimport Matroid +from sage.matroids.set_system cimport SetSystem + +cdef class CircuitsMatroid(Matroid): + cdef frozenset _groundset # _E + cdef int _matroid_rank # _R + cdef SetSystem _C # circuits + cdef dict _k_C # k-circuits (k=len) + cdef bint _nsc_defined + cpdef groundset(self) noexcept + cpdef _rank(self, X) noexcept + cpdef full_rank(self) noexcept + cpdef _is_independent(self, F) noexcept + cpdef _max_independent(self, F) noexcept + cpdef _circuit(self, F) noexcept + + # enumeration + cpdef bases(self) noexcept + cpdef circuits(self, k=*) noexcept + cpdef nonspanning_circuits(self) noexcept + + # properties + cpdef girth(self) noexcept + cpdef is_paving(self) noexcept + + # isomorphism + cpdef _is_isomorphic(self, other, certificate=*) noexcept + + # verification + cpdef is_valid(self) noexcept diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx new file mode 100644 index 00000000000..3c211cc74d3 --- /dev/null +++ b/src/sage/matroids/circuits_matroid.pyx @@ -0,0 +1,645 @@ +r""" +Circuits matroids + +Matroids are characterized by a list of circuits, which are minimal dependent +sets. The CircuitsMatroid class implements matroids using this information as +data. + +A ``CircuitsMatroid`` can be created from another matroid or from a list of +circuits. For a full description of allowed inputs, see +:class:`below `. It is +recommended to use the :func:`Matroid() ` +function for a more flexible construction of a ``CircuitsMatroid``. For direct +access to the ``CircuitsMatroid`` constructor, run:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + +AUTHORS: + +- Giorgos Mousa (2023-12-23): initial version + +TESTS:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Fano()) + sage: TestSuite(M).run() +""" + +# **************************************************************************** +# Copyright (C) 2023 Giorgos Mousa +# +# 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/ +# **************************************************************************** + +from sage.structure.richcmp cimport rich_to_bool, richcmp +from sage.matroids.matroid cimport Matroid +from sage.matroids.set_system cimport SetSystem +from cpython.object cimport Py_EQ, Py_NE + + +cdef class CircuitsMatroid(Matroid): + r""" + INPUT: + + - ``M`` -- a matroid (default: ``None``) + - ``groundset`` -- a list (default: ``None``); the groundset of the matroid + - ``circuits`` -- a list (default: ``None``); the collection of circuits of + the matroid + + OUTPUT: + + - If the input is a matroid ``M``, return a ``CircuitsMatroid`` instance + representing ``M``. + - Otherwise, return a ``CircuitsMatroid`` instance based on the + ``groundset`` and ``circuits``. + + .. NOTE:: + + For a more flexible means of input, use the ``Matroid()`` function. + """ + + # necessary (__init__, groundset, _rank) + + def __init__(self, M=None, groundset=None, circuits=None, nsc_defined=False): + """ + Initialization of the matroid. See class docstring for full + documentation. + """ + if M is not None: + self._groundset = frozenset(M.groundset()) + self._C = SetSystem(list(M.groundset()), frozenset([frozenset(C) for C in M.circuits()])) + else: + self._groundset = frozenset(groundset) + self._C = SetSystem(list(groundset), frozenset([frozenset(C) for C in circuits])) + # k-circuits + self._k_C = {} + for C in self._C: + try: + self._k_C[len(C)] += [C] + except KeyError: + self._k_C[len(C)] = [] + self._k_C[len(C)] += [C] + self._matroid_rank = self.rank(self._groundset) + self._nsc_defined = nsc_defined + + cpdef groundset(self) noexcept: + """ + Return the groundset of the matroid. + + The groundset is the set of elements that comprise the matroid. + + OUTPUT: + + a set + + EXAMPLES:: + + sage: M = matroids.Theta(2) + sage: sorted(M.groundset()) + ['x0', 'x1', 'y0', 'y1'] + """ + return self._groundset + + cpdef _rank(self, X) noexcept: + """ + Return the rank of a set ``X``. + + This method does no checking on ``X``, and ``X`` may be assumed to have + the same interface as ``frozenset``. + + INPUT: + + - ``X`` -- an object with Python's ``frozenset`` interface + + OUTPUT: + + an integer; the rank of ``X`` in the matroid + + EXAMPLES:: + + sage: M = matroids.Theta(3) + sage: M._rank(['x1', 'y0', 'y2']) + 2 + """ + return len(self._max_independent(X)) + + # optional + + cpdef full_rank(self) noexcept: + r""" + Return the rank of the matroid. + + The *rank* of the matroid is the size of the largest independent + subset of the groundset. + + OUTPUT: + + an integer; the rank of the matroid + + EXAMPLES:: + + sage: M = matroids.Theta(20) + sage: M.full_rank() + 20 + """ + return self._matroid_rank + + cpdef _is_independent(self, F) noexcept: + """ + Test if input is independent. + + INPUT: + + - ``X`` -- An object with Python's ``frozenset`` interface containing + a subset of ``self.groundset()`` + + OUTPUT: + + boolean + """ + I = set(F) + s = len(F) + for i in self._k_C: + if i <= s: + for C in self._k_C[i]: + if C <= I: + return False + return True + + cpdef _max_independent(self, F) noexcept: + """ + Compute a maximal independent subset. + + INPUT: + + - ``X`` -- An object with Python's ``frozenset`` interface containing + a subset of ``self.groundset()`` + + OUTPUT: + + a frozenset; a maximal independent subset of ``X`` + """ + I = set(F) + for i in self._k_C: + for C in self._k_C[i]: + if i <= len(I) and i > 0: + if C <= I: + for e in C: + break + I.remove(e) + + return frozenset(I) + + cpdef _circuit(self, F) noexcept: + """ + Return a minimal dependent subset. + + INPUT: + + - ``X`` -- An object with Python's ``frozenset`` interface containing + a subset of ``self.groundset()``. + + OUTPUT: + + a frozenset; a circuit contained in ``X``, if it exists. Otherwise an + error is raised. + """ + I = set(F) + for C in self.circuits(): + if C <= I: + return C + raise ValueError("no circuit in independent set") + + cpdef _is_isomorphic(self, other, certificate=False) noexcept: + """ + Test if ``self`` is isomorphic to ``other``. + + INPUT: + + - ``other`` -- a matroid + - ``certificate`` -- boolean (optional) + + OUTPUT: + + boolean, and, if certificate = True, a dictionary giving the + isomorphism or None + + .. NOTE:: + + Internal version that does no input checking. + """ + if certificate: + return self._is_isomorphic(other), self._isomorphism(other) + N = CircuitsMatroid(other) + return self._C._isomorphism(N._C) is not None + + # representation + + def _repr_(self): + """ + Return a string representation of the matroid. + """ + if self._nsc_defined: + return Matroid._repr_(self) + " with " + str(len(self.nonspanning_circuits())) + " non-spanning circuits" + else: + return Matroid._repr_(self) + " with " + str(len(self._C)) + " circuits" + + # comparison + + def __hash__(self): + r""" + Return an invariant of the matroid. + + This function is called when matroids are added to a set. It is very + desirable to override it so it can distinguish matroids on the same + groundset, which is a very typical use case! + + .. WARNING:: + + This method is linked to __richcmp__ (in Cython) and __cmp__ or + __eq__/__ne__ (in Python). If you override one, you should + (and in Cython: MUST) override the other! + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: N = CircuitsMatroid(matroids.catalog.Vamos()) + sage: hash(M) == hash(N) + True + sage: O = CircuitsMatroid(matroids.catalog.NonVamos()) + sage: hash(M) == hash(O) + False + """ + return hash(tuple([self.groundset(), frozenset(self._C)])) + + def __richcmp__(left, right, int op): + r""" + Compare two matroids. + + We take a very restricted view on equality: the objects need to be of + the exact same type (so no subclassing) and the internal data need to + be the same. For CircuitsMatroids, this means that the groundsets and + the sets of circuits of the two matroids are equal. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Pappus()) + sage: N = CircuitsMatroid(matroids.catalog.NonPappus()) + sage: M == N + False + sage: N = Matroid(circuits=M.circuits()) + sage: M == N + True + """ + cdef CircuitsMatroid lt, rt + if op not in [Py_EQ, Py_NE]: + return NotImplemented + if type(left) is not type(right): + return NotImplemented + lt = left + rt = right + if lt.groundset() != rt.groundset(): + return rich_to_bool(op, 1) + if lt.full_rank() != rt.full_rank(): + return rich_to_bool(op, 1) + return richcmp(frozenset(lt._C), frozenset(rt._C), op) + + # copying, loading, saving + + def __copy__(self): + """ + Create a shallow copy. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: N = copy(M) # indirect doctest + sage: M == N + True + sage: M.groundset() is N.groundset() + True + """ + N = CircuitsMatroid(groundset=[], circuits=[]) + N._groundset = self._groundset + N._C = self._C + N._k_C = self._k_C + N._nsc_defined = self._nsc_defined + N._matroid_rank = self._matroid_rank + N.rename(self.get_custom_name()) + return N + + def __deepcopy__(self, memo=None): + """ + Create a deep copy. + + .. NOTE:: + + Since matroids are immutable, a shallow copy normally suffices. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: N = deepcopy(M) # indirect doctest + sage: M == N + True + sage: M.groundset() is N.groundset() + False + """ + if memo is None: + memo = {} + from copy import deepcopy + # Since matroids are immutable, N cannot reference itself in correct code, so no need to worry about the recursion. + N = CircuitsMatroid(groundset=deepcopy(self._groundset, memo), circuits=deepcopy(frozenset(self._C), memo)) + N.rename(deepcopy(self.get_custom_name(), memo)) + return N + + def __reduce__(self): + """ + Save the matroid for later reloading. + + OUTPUT: + + A tuple ``(unpickle, (version, data))``, where ``unpickle`` is the + name of a function that, when called with ``(version, data)``, + produces a matroid isomorphic to ``self``. ``version`` is an integer + (currently 0) and ``data`` is a tuple ``(E, C, name)`` where ``E`` is + the groundset, ``C`` is the list of circuits, and ``name`` is a custom + name. + + EXAMPLES:: + + sage: M = matroids.Theta(5) + sage: M == loads(dumps(M)) # indirect doctest + True + sage: M.reset_name() + sage: loads(dumps(M)) + Matroid of rank 5 on 10 elements with 45 circuits + """ + import sage.matroids.unpickling + data = (self._groundset, frozenset(self._C), self.get_custom_name()) + version = 0 + return sage.matroids.unpickling.unpickle_circuits_matroid, (version, data) + + # enumeration + + cpdef bases(self) noexcept: + r""" + Return the bases of the matroid. + + OUTPUT: + + an iterable + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + """ + cdef SetSystem B + B = SetSystem(list(self.groundset())) + from itertools import combinations + for S in combinations(self._groundset, self._matroid_rank): + flag = True + for C in self.nonspanning_circuits(): + if C <= set(S): + flag = False + break + if flag: + B.append(S) + return B + + def bases_iterator(self): + r""" + Return the bases of the matroid. + + OUTPUT: + + an iterable + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + """ + from itertools import combinations + for B in combinations(self._groundset, self._matroid_rank): + flag = True + for C in self.nonspanning_circuits(): + if C <= set(B): + flag = False + break + if flag: + yield frozenset(B) + + cpdef circuits(self, k=None) noexcept: + """ + Return the list of circuits of the matroid. + + OUTPUT: + + a SetSystem + """ + cdef SetSystem C + C = SetSystem(list(self.groundset())) + if k: + for c in self._k_C[k]: + C.append(c) + else: + for i in self._k_C: + for c in self._k_C[i]: + C.append(c) + return C + + def circuits_iterator(self, k=None): + """ + Return an iterator over the circuits of the matroid. + + OUTPUT: + + an iterator + """ + if k: + for C in self._k_C[k]: + yield C + else: + for i in self._k_C: + for C in self._k_C[i]: + yield C + + cpdef nonspanning_circuits(self) noexcept: + """ + Return the list of nonspanning circuits of the matroid. + + OUTPUT: + + a SetSystem + """ + cdef SetSystem NSC + NSC = SetSystem(list(self.groundset())) + for i in self._k_C: + if i <= self.rank(): + for C in self._k_C[i]: + NSC.append(C) + return NSC + + def no_broken_circuits_sets(self, order=None): + r""" + Return the no broken circuits (NBC) sets of ``self``. + + An NBC set is a subset `A` of the ground set under some total + ordering `<` such that `A` contains no broken circuit. + + INPUT: + + - ``order`` -- a total ordering of the groundset given as a list + + EXAMPLES:: + + sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) + sage: SimplicialComplex(M.no_broken_circuits_sets()) + Simplicial complex with vertex set (1, 2, 3, 4, 5) + and facets {(1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5)} + sage: SimplicialComplex(M.no_broken_circuits_sets([5,4,3,2,1])) + Simplicial complex with vertex set (1, 2, 3, 4, 5) + and facets {(1, 3, 5), (1, 4, 5), (2, 3, 5), (2, 4, 5)} + + :: + + sage: M = Matroid(circuits=[[1,2,3], [1,4,5], [2,3,4,5]]) + sage: SimplicialComplex(M.no_broken_circuits_sets([5,4,3,2,1])) + Simplicial complex with vertex set (1, 2, 3, 4, 5) + and facets {(1, 3, 5), (2, 3, 5), (2, 4, 5), (3, 4, 5)} + """ + if order is None: + order = sorted(self.groundset(), key=str) + else: + if frozenset(order) != self.groundset(): + raise ValueError("not an ordering of the groundset") + + # compute broken circuits + BC = [] + for C in self.circuits(): + for e in order: + if e in C: + BC.append(C - set([e])) + break + + # the facets suffice + for B in self.bases(): + flag = True + for bc in BC: + if bc <= B: + flag = False + break + if flag: + yield B + + # properties + + cpdef girth(self) noexcept: + r""" + Return the girth of the matroid. + + The girth is the size of the smallest circuit. In case the matroid has + no circuits the girth is `\infty`. + + EXAMPLES:: + + sage: matroids.Theta(10).girth() + 3 + + REFERENCES: + + [Oxl2011]_, p. 327. + """ + return min([i for i in self._k_C], default=float('inf')) + + cpdef is_paving(self) noexcept: + """ + Return if ``self`` is paving. + + A matroid is paving if each of its circuits has size `r` or `r+1`. + + OUTPUT: + + boolean + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Vamos()) + sage: M.is_paving() + True + """ + return self.girth() >= self.rank() + + # verification + + cpdef is_valid(self) noexcept: + r""" + Test if self obeys the matroid axioms. + + For a matroid defined by its circuits, we check the circuit axioms. + + OUTPUT: + + boolean + + EXAMPLES:: + + sage: C = [[1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + True + sage: C = [[1,2], [1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + sage: C = [[3,6], [1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + sage: C = [[3,6], [1, 2, 3], [3, 4, 5], [1, 2, 6], [6, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + True + sage: C = [[], [1, 2, 3], [3, 4, 5], [1, 2, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + sage: C = [[1, 2, 3], [3, 4, 5]] + sage: M = Matroid(circuits=C) + sage: M.is_valid() + False + """ + for i in self._k_C: + for j in self._k_C: + if i <= j: + for C1 in self._k_C[i]: + if len(C1) == 0: + return False + for C2 in self._k_C[j]: + if C1 < C2: + return False + if C1 == C2: + break + for e in C1 & C2: + flag = False + S = (set(C1) | set(C2)) - {e} + for k in self._k_C: + if k <= len(S) and not flag: + for C3 in self._k_C[k]: + if C3 <= S: + flag = True + break + if not flag: + return False + return True diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index 34368c34f91..7d25f96e9aa 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -112,6 +112,7 @@ import sage.matroids.matroid import sage.matroids.basis_exchange_matroid from .rank_matroid import RankMatroid +from .circuits_matroid import CircuitsMatroid from .circuit_closures_matroid import CircuitClosuresMatroid from .basis_matroid import BasisMatroid from .linear_matroid import LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid @@ -302,21 +303,25 @@ def Matroid(groundset=None, data=None, **kwds): :: sage: M1 = Matroid(groundset='abc', circuits=['bc']) - sage: M2 = Matroid(bases=['ab', 'ac']) - sage: M1 == M2 - True A matroid specified by a list of circuits gets converted to a - :class:`BasisMatroid ` + :class:`CircuitsMatroid ` internally:: + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M2 = CircuitsMatroid(Matroid(bases=['ab', 'ac'])) + sage: M1 == M2 + True + + + sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd', ....: 'bcd']) sage: type(M) - <... 'sage.matroids.basis_matroid.BasisMatroid'> + Strange things can happen if the input does not satisfy the circuit - axioms, and these are not always caught by the + axioms, and these can be caught by the :meth:`is_valid() ` method. So always check whether your input makes sense! @@ -324,11 +329,7 @@ def Matroid(groundset=None, data=None, **kwds): sage: M = Matroid('abcd', circuits=['ab', 'acd']) sage: M.is_valid() - True - sage: [sorted(C) for C in M.circuits()] # random - [['a']] - - + False #. Graph: @@ -762,17 +763,7 @@ def Matroid(groundset=None, data=None, **kwds): groundset = set() for C in data: groundset.update(C) - # determine the rank by computing a basis element - b = set(groundset) - for C in data: - I = b.intersection(C) - if len(I) >= len(C): - b.discard(I.pop()) - rk = len(b) - # Construct the basis matroid of appropriate rank. Note: slow! - BB = [frozenset(B) for B in combinations(groundset, rk) - if not any(frozenset(C).issubset(B) for C in data)] - M = BasisMatroid(groundset=groundset, bases=BB) + M = CircuitsMatroid(groundset=groundset, circuits=data) # Nonspanning circuits: elif key == 'nonspanning_circuits': @@ -796,10 +787,13 @@ def Matroid(groundset=None, data=None, **kwds): break if flag: B += [list(b)] - M = BasisMatroid(groundset=groundset, bases=B) + # convert to circuits matroid defined by non-spanning circuits + M = CircuitsMatroid( + BasisMatroid(groundset=groundset, bases=B), + nsc_defined=True + ) # Graphs: - elif key == 'graph': from sage.graphs.graph import Graph diff --git a/src/sage/matroids/database_matroids.py b/src/sage/matroids/database_matroids.py index 1272efefff5..73ef805ea34 100644 --- a/src/sage/matroids/database_matroids.py +++ b/src/sage/matroids/database_matroids.py @@ -1308,7 +1308,7 @@ def R9(): EXAMPLES:: sage: M = matroids.catalog.R9(); M - R9: Matroid of rank 3 on 9 elements with 69 bases + R9: Matroid of rank 3 on 9 elements with 15 non-spanning circuits sage: M.is_valid() True sage: len(M.nonspanning_circuits()) @@ -2105,7 +2105,7 @@ def Spike(r, t=True, C3=[]): INPUT: - ``r`` -- an integer (`r \ge 3`); the rank of the spike - - ``t`` -- a boolean (default: ``True``); whether the spike is tipped + - ``t`` -- boolean (default: ``True``); whether the spike is tipped - ``C3`` -- a list (default: ``[]``); a list of extra nonspanning circuits. The default (i.e. the empty list) results in a free `r`-spike @@ -2116,7 +2116,8 @@ def Spike(r, t=True, C3=[]): EXAMPLES:: sage: M = matroids.Spike(3, False); M - Free 3-spike\t: Matroid of rank 3 on 6 elements with 20 bases + Free 3-spike\t: M \ {'t'}, where M is Matroid of rank 3 on 7 elements + with 3 non-spanning circuits sage: M.is_isomorphic(matroids.Uniform(3, 6)) True sage: len(matroids.Spike(8).bases()) diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx index d49cf378a63..c791c3aa660 100644 --- a/src/sage/matroids/unpickling.pyx +++ b/src/sage/matroids/unpickling.pyx @@ -28,13 +28,15 @@ AUTHORS: from sage.data_structures.bitset_base cimport * import sage.matroids.matroid import sage.matroids.basis_exchange_matroid -from sage.matroids.minor_matroid import MinorMatroid -from sage.matroids.dual_matroid import DualMatroid -from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid from sage.matroids.basis_matroid cimport BasisMatroid -from sage.matroids.linear_matroid cimport LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid +from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid +from sage.matroids.circuits_matroid cimport CircuitsMatroid +from sage.matroids.dual_matroid import DualMatroid from sage.matroids.lean_matrix cimport GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, PlusMinusOneMatrix, RationalMatrix from sage.matroids.graphic_matroid import GraphicMatroid +from sage.matroids.linear_matroid cimport LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid +from sage.matroids.minor_matroid import MinorMatroid + from sage.rings.rational cimport Rational from sage.libs.gmp.mpq cimport mpq_set @@ -88,6 +90,49 @@ def unpickle_basis_matroid(version, data): return M +############################################################################# +# CircuitsMatroid +############################################################################# + +def unpickle_circuits_matroid(version, data): + """ + Unpickle a CircuitsMatroid. + + *Pickling* is Python's term for the loading and saving of objects. + Functions like these serve to reconstruct a saved object. This all happens + transparently through the ``load`` and ``save`` commands, and you should + never have to call this function directly. + + INPUT: + + - ``version`` -- an integer, expected to be 0 + - ``data`` -- a tuple ``(E, C, name)`` in which ``E`` is the groundset + of the matroid, ``C`` is the list of circuits , and ``name`` is a custom + name. + + OUTPUT: + + A matroid. + + .. WARNING:: + + Users should never call this function directly. + + EXAMPLES:: + + sage: M = matroids.Theta(5) + sage: M == loads(dumps(M)) # indirect doctest + True + """ + cdef CircuitsMatroid M + if version != 0: + raise TypeError("object was created with newer version of Sage. Please upgrade.") + M = CircuitsMatroid(groundset=data[0], circuits=data[1]) + if data[2] is not None: + M.rename(data[2]) + return M + + ############################################################################# # CircuitClosuresMatroid ############################################################################# From 852944b1a1626e0ef2309a27c7804b9d72bc31a2 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Fri, 26 Jan 2024 14:34:29 +0000 Subject: [PATCH 030/191] Ugly patch for characteristic two in any_root --- .../rings/polynomial/polynomial_element.pyx | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4c9cf2f7e0d..81097c2ee65 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2285,27 +2285,39 @@ cdef class Polynomial(CommutativePolynomial): # now self has only roots of degree ``degree``. # for now, we only implement the Cantor-Zassenhaus split k = self.degree() // degree - if k == 1: - try: - return self.roots(ring, multiplicities=False)[0] # is there something better to do here? - except IndexError: - raise ValueError("no roots F %s" % self) - if q % 2 == 0: - while True: - T = R.random_element(2*degree-1) - if T == 0: - continue - T = T.monic() - C = T - for i in range(degree-1): - C = T + pow(C,q,self) - h = self.gcd(C) - hd = h.degree() - if hd != 0 and hd != self.degree(): - if 2*hd <= self.degree(): - return h.any_root(ring, -degree, True) - else: - return (self//h).any_root(ring, -degree, True) + if k == 1 or q % 2 == 0: + # Is there something better to do here for k = 1? + roots = self.roots(ring, multiplicities=False) + if roots: + from sage.misc.prandom import choice + return choice(roots) + else: + raise ValueError(f"no roots F {self}" % self) + # Giacomo Pope (26-1-2024): + # This is buggy and very slow, I would like to fix it, but as far + # as I can tell, calling roots for GF(2^k) and picking one is simply + # faster and more robust. + # if q % 2 == 0: + # while True: + # T = R.random_element(2*degree-1) + # if T == 0: + # continue + # T = T.monic() + # # Compute the trace of T with field of order 2^k + # # sum T^(2^i) for i in range (degree * k) + # # We use repeated squaring to avoid redundent multiplications + # C = T + # TT = T + # for _ in range(degree * self.base_ring().degree() - 1): + # TT = pow(TT, 2, self) + # C += TT + # h = self.gcd(C) + # hd = h.degree() + # if hd != 0 and hd != self.degree(): + # if 2*hd <= self.degree(): + # return h.any_root(ring, -degree, True) + # else: + # return (self//h).any_root(ring, -degree, True) else: while True: T = R.random_element(2*degree-1) From 356c881672fa5f1e468750d0ba5583b33dd94a4c Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Sat, 27 Jan 2024 23:12:58 +0000 Subject: [PATCH 031/191] Simply rewrite any_root, as it's easier than debugging the old code --- .../rings/polynomial/polynomial_element.pyx | 304 ++++++++---------- 1 file changed, 140 insertions(+), 164 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 81097c2ee65..2154e197494 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2168,174 +2168,150 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: no roots (non-field) x^2 + 1 """ - if self.base_ring().is_finite() and self.base_ring().is_field(): - if self.degree() < 0: - return ring(0) - if self.degree() == 0: - raise ValueError("no roots A %s" % self) - if not assume_squarefree: - SFD = self.squarefree_decomposition() - SFD.sort() - for f, e in SFD: - try: - return f.any_root(ring, degree, True) - except ValueError: - pass - if self.degree() == 1 and (degree is None or degree == 1): - if ring is None: - return -self.get_unsafe(0) / self.get_unsafe(1) - else: - return ring(-self.get_unsafe(0) / self.get_unsafe(1)) - q = self.base_ring().order() - if ring is None: - allowed_deg_mult = Integer(1) - else: - if not (self.base_ring().is_field() and self.base_ring().is_finite()): - raise NotImplementedError - if ring.characteristic() != self.base_ring().characteristic(): - raise ValueError("ring must be an extension of the base ring") - if not (ring.is_field() and ring.is_finite()): - raise NotImplementedError - allowed_deg_mult = Integer(ring.factored_order()[0][1]) # generally it will be the quotient of this by the degree of the base ring. - if degree is None: - x = self._parent.gen() - if allowed_deg_mult == 1: - xq = pow(x,q,self) - self = self.gcd(xq-x) - degree = -1 - if self.degree() == 0: - raise ValueError("no roots B %s" % self) - else: - xq = x - d = Integer(0) - while True: - # one pass for roots that actually lie within ring. - e = self.degree() - if 2*d+2 > e: - # this polynomial has no factors dividing allowed_deg_mult - if allowed_deg_mult % e == 0: - degree = -e - break - while d < allowed_deg_mult: - d = d+1 - xq = pow(xq,q,self) - if d.divides(allowed_deg_mult): - break - A = self.gcd(xq-x) - if A != 1: - self = A - degree = -d - break - if d == allowed_deg_mult: - break - if degree is None: - if allowed_deg_mult == 1: - raise ValueError("no roots C %s" % self) - xq = x - d = Integer(0) - while True: - # now another for roots that will lie in an extension. - e = self.degree() - if 2*d+2 > e: - # this polynomial is irreducible. - degree = -e - break - while True: - # we waste a little effort here in computing the xq again. - d = d+1 - xq = pow(xq,q,self) - if allowed_deg_mult.divides(d): - break - A = self.gcd(xq-x) - if A != 1: - self = A - degree = -d - break - if degree == 0: - raise ValueError("degree should be nonzero") - R = self._parent - x = R.gen() - if degree > 0: - xq = x - d = 0 - while True: - e = self.degree() - if 2*d > e: - if degree != e: - raise ValueError("no roots D %s" % self) - break - d = d+1 - xq = pow(xq,q,self) - if d == degree: - break - A = self.gcd(xq-x) - if A != 1: - self = self // A - if d == degree: - self = self.gcd(xq-x) - if self.degree() == 0: - raise ValueError("no roots E %s" % self) - else: - degree = -degree + # Currently I can't see a benefit of the degree method, so I think we should + # remove it + if degree is not None: + print(f"Warning: 'degree' will soon be deprecated as it is no longer needed") + + # When not working over a finite field, do the simple + # thing of factoring for roots and picking the first + # root. If none available, raise an error. + from sage.categories.finite_fields import FiniteFields + if not self.base_ring() in FiniteFields(): + rs = self.roots(ring=ring, multiplicities=False) + if rs: + return rs[0] + raise ValueError("polynomial {self} has no roots" % self) + + # For finite fields, we find roots in the following three steps: + # + # 1. Compute the squarefree decomposition of the polynomial + # 2. For each squarefree polynomial find the distinct degree = 1 + # factorison, F, which is the product of degree one polynomials + # dividing the squarefree polynomial + # 3. Using Cantor-Zassenhaus splitting with degree one to find a + # single linear factor and return the root. + # + # These steps are performed by the following helper functions + # + # ======================== # + # Begin helper functions # + # ======================== # + def _linear_root(f, ring=None): if ring is None: - if degree == 1: - ring = self.base_ring() - else: - ring = self.base_ring().extension(degree) # this won't work yet. - # now self has only roots of degree ``degree``. - # for now, we only implement the Cantor-Zassenhaus split - k = self.degree() // degree - if k == 1 or q % 2 == 0: - # Is there something better to do here for k = 1? - roots = self.roots(ring, multiplicities=False) - if roots: - from sage.misc.prandom import choice - return choice(roots) - else: - raise ValueError(f"no roots F {self}" % self) - # Giacomo Pope (26-1-2024): - # This is buggy and very slow, I would like to fix it, but as far - # as I can tell, calling roots for GF(2^k) and picking one is simply - # faster and more robust. - # if q % 2 == 0: - # while True: - # T = R.random_element(2*degree-1) - # if T == 0: - # continue - # T = T.monic() - # # Compute the trace of T with field of order 2^k - # # sum T^(2^i) for i in range (degree * k) - # # We use repeated squaring to avoid redundent multiplications - # C = T - # TT = T - # for _ in range(degree * self.base_ring().degree() - 1): - # TT = pow(TT, 2, self) - # C += TT - # h = self.gcd(C) - # hd = h.degree() - # if hd != 0 and hd != self.degree(): - # if 2*hd <= self.degree(): - # return h.any_root(ring, -degree, True) - # else: - # return (self//h).any_root(ring, -degree, True) - else: - while True: - T = R.random_element(2*degree-1) - if T == 0: - continue + return -f[0] / f[1] + return ring(-f[0] / f[1]) + + def _any_root_squarefree(f, ring=None): + # Compute the distinct degree factorisation for degree one, + # the output will be the product of #all degree one + # irreducible polynomials dividing f + q = f.base_ring().order() # p^k + R, X = f.parent().objgen() + w = pow(X, q, f) - X + g = f.gcd(w) + + # When the degree is one, we have found our + # root. If the degree is zero, there are no roots + if g.degree().is_one(): + return _linear_root(g, ring=ring) + elif g.degree().is_zero(): + raise ValueError(f"no root can be computed for {f}") + + # Otherwise, we can find a root from the CZ splitting of g + return _cantor_zassenhaus_split_root(g, 1, ring=ring) + + def _cantor_zassenhaus_split_root(f, degree, ring=None): + R = f.parent() + q = f.base_ring().order() + # Need to handle odd and even charcteristic separately + if q % 2 != 0: + # We expect to succeed with 1/2 probability, so if we + # try 1000 times and fail, there's a bug somewhere. + for _ in range(1000): + T = R.random_element(2*degree + 1) + if T.is_zero(): continue # skip T = 0 T = T.monic() - h = self.gcd(pow(T, Integer((q**degree-1)/2), self)-1) + + # Compute the gcd. 50% chance this is a non-trivial factor of f + h = f.gcd(pow(T, (q-1)//2, f)-1) hd = h.degree() - if hd != 0 and hd != self.degree(): - if 2*hd <= self.degree(): - return h.any_root(ring, -degree, True) + + # If we found a degree-one root, return it + if hd.is_one(): + return _linear_root(h, ring=ring) + + # Else check if we have a non-trivial factor and keep going + if not hd.is_zero() and hd != f.degree(): + if 2*hd <= f.degree(): + return _cantor_zassenhaus_split_root(h, degree, ring=ring) else: - return (self//h).any_root(ring, -degree, True) - else: - rs = self.roots(ring=ring, multiplicities=False) - if rs: - return rs[0] - raise ValueError("no roots (non-field) %s" % self) + return _cantor_zassenhaus_split_root(f//h, degree, ring=ring) + # If you are reaching this error, chances are there's a bug in the code. + raise ValueError(f"no splitting of degree {degree} found for {f}") + + # Now handle even charactertistic + for _ in range(1000): + # Sample a uniformly random element of R + T = R.random_element(2*degree + 1) + if T.is_zero(): continue + T = T.monic() + + # Compute the trace of T with field of order 2^k + # sum T^(2^i) for i in range (degree * k) + # We use repeated squaring to avoid redundent multiplications + C, TT = T, T + for _ in range(degree * f.base_ring().degree() - 1): + TT = pow(TT, 2, f) + C += TT + + # Compute the gcd to find a factor + h = f.gcd(C) + hd = h.degree() + + # If we found a degree-one root, return it + if hd.is_one(): + return _linear_root(h, ring=ring) + + # else check if we have a factor + elif not hd.is_zero() and hd != f.degree(): + if 2*hd <= f.degree(): + return _cantor_zassenhaus_split_root(h, degree, ring) + else: + return _cantor_zassenhaus_split_root(f // h, degree, ring) + + # If you are reaching this error, chances are there's a bug in the code. + raise ValueError(f"no splitting of degree {degree} found for {f}") + # ======================== # + # End helper functions # + # ======================== # + + # Initial checks for bad input + if self.degree() < 0: + if ring is None: + return self.base_ring()(0) + return ring(0) + elif self.degree() == 0: + raise ValueError(f"no root can be computed for {self}") + + # If the input is a linear polynomial, simply compute the root + if self.degree() == 1: + return _linear_root(self, ring=ring) + + # If we know the polynomial is square-free, we can start here + if assume_squarefree: + return _any_root_squarefree(self, ring=ring) + + # Otherwise we compute the squarefree decomposition and check each + # polynomial for a root. If no poly has a root, we raise an error. + SFD = self.squarefree_decomposition() + SFD.sort() + for poly, _ in SFD: + try: + return _any_root_squarefree(poly, ring=ring) + except ValueError: + pass + raise ValueError(f"no root can be computed for {self}") def __truediv__(left, right): r""" From 661efd84b8f10940721175f2bc74f0493618f23c Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Sat, 27 Jan 2024 23:17:01 +0000 Subject: [PATCH 032/191] handle deprecation properly --- src/sage/rings/polynomial/polynomial_element.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 2154e197494..531dbf2f65f 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2171,7 +2171,8 @@ cdef class Polynomial(CommutativePolynomial): # Currently I can't see a benefit of the degree method, so I think we should # remove it if degree is not None: - print(f"Warning: 'degree' will soon be deprecated as it is no longer needed") + from sage.misc.superseded import deprecation + deprecation(37170, "'degree' will soon be deprecated as it is no longer needed") # When not working over a finite field, do the simple # thing of factoring for roots and picking the first From a8dcf5ac993c737b10a209d4d97fbceca44bf36f Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:10:33 +0000 Subject: [PATCH 033/191] Include any_irreducible_factor and refactor any_root to simply wrap this --- .../rings/polynomial/polynomial_element.pyx | 352 +++++++++++------- 1 file changed, 218 insertions(+), 134 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 531dbf2f65f..6211248c42b 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2047,6 +2047,198 @@ cdef class Polynomial(CommutativePolynomial): else: return (False, None) if root else False + def _distinct_degree_factorisation_squarefree(self): + """ + Helper function for any_irreducible_factor which computes + the distinct degree factorisation of `self`. + + Creates an iterator for all valid degrees `d`, and return + tuples of the form `(a_d, d)` for a polynomial `a_d` the + product of irreducible polynomials of degree `d` + + Assumes that self is squarefree. + """ + q = self.base_ring().order() # p^k + R, x = self.parent().objgen() + + # Initialise values + d = 0 + v = self + w = x + + # Iterate over all possible degrees with degree + while e <= 2*(d + 1): + e = v.degree() + d = d + 1 + w = pow(w, q, v) + + ad = v.gcd(w - x) + + if not ad.is_one(): + v = v // ad + w = w % v + + yield (ad, d) + + # Last case, v itself might be irreducible + e = v.degree() + if e > 0: + yield (v, e) + return + + def _cantor_zassenhaus_split_to_irreducible(self, degree): + """ + Helper function for any_irreducible_factor which computes + a factor from a polynomial of the form `self = prod g_i(x)` + with all `g_i(x)` having the same degree. Uses the Cantor + Zassenhaus splitting method. + """ + R = self.parent() + q = self.base_ring().order() + + # Polynomial is already irreducible by the assumptions of the function + if self.degree() == degree: + return self + + # We expect to succeed with greater than 1/2 probability, + #so if we try 1000 times and fail, there's a bug somewhere. + for _ in range(1000): + T = R.random_element(2*degree + 1) + if T.is_zero(): + continue + T = T.monic() + + # Need to handle odd and even charcteristic separately + if q % 2 != 0: + h = self.gcd(pow(T, (q-1)//2, self)-1) + else: + # Compute the trace of T with field of order 2^k + # sum T^(2^i) for i in range (degree * k) + # We use repeated squaring to avoid redundent multiplications + C, TT = T, T + for _ in range(degree * self.base_ring().degree() - 1): + TT = pow(TT, 2, self) + C += TT + h = self.gcd(C) + + hd = h.degree() + + # If we found a factor of desired degree, return it + if hd == degree: + return h + + # Else check if we have a non-trivial factor and keep going + if not hd.is_zero() and hd != self.degree(): + if 2*hd <= self.degree(): + return h._cantor_zassenhaus_split_to_irreducible(degree) + else: + return (self//h)._cantor_zassenhaus_split_to_irreducible(degree) + + # If you are reaching this error, chances are there's a bug in the code. + raise AssertionError(f"no splitting of degree {degree} found for {self}") + + def _any_irreducible_factor_squarefree(self, degree=None): + """ + TODO + """ + # If the degree is not None we only want to check a single polynomial + if degree is not None: + for (poly, d) in self._distinct_degree_factorisation_squarefree(): + if d == degree and not poly.is_one(): + return poly._cantor_zassenhaus_split_to_irreducible(degree) + # Stop iterating early if the degree is too large + elif d > degree: + raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") + raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") + + # Otherwise we check all degrees, starting from the smallest + for (poly, d) in self._distinct_degree_factorisation_squarefree(): + # Skip the split checking if `_distinct_degree_factorisation_squarefree` + # has found no elements of degree `d` + if poly.is_one(): + continue + + # Otherwise find a factor from the distinct degree factor + return poly._cantor_zassenhaus_split_to_irreducible(d) + + raise ValueError(f"no irreducible factor could be computed from {self}") + + def any_irreducible_factor(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): + """ + TODO + """ + # If the ring is not None, then first change ring and run the algorithm again + if ring is not None: + try: + f = self.change_ring(ring) + return f.any_irreducible_factor(None, degree, assume_squarefree, assume_distinct_deg) + except (TypeError, ValueError): + raise TypeError(f"unable to coerce from {self.base_ring()} to {ring}") + + # Make sure the user inputted something reasonable for degree + if degree is not None: + degree = ZZ(degree) + if degree < 1: + raise ValueError(f"{degree = } must be positive") + + if assume_distinct_deg and degree is None: + raise ValueError("degree must be known if distinct degree factorisation is assumed") + + # When not working over a finite field, do the simple thing of factoring. + # If degree is none, we return the first factor, otherwise we iterate + # through and look for a polynomial with the desired degree. + from sage.categories.finite_fields import FiniteFields + if self.base_ring() not in FiniteFields(): + try: + factorisation = self.factor() + except (NotImplementedError, ValueError): + raise ValueError(f"Cannot factor {self} over the base ring {self.base_ring()}") + if degree is None: + return factorisation[0][0] + for (poly, e) in factorisation: + if poly.degree() == degree: + return poly + raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") + + # For finite fields, we find irreducible factors in the following three steps: + # + # 1. Compute the squarefree decomposition of the polynomial `self` + # 2. For each squarefree polynomial find the distinct degree `d` + # factorison, F, which is the product of degree `d` polynomials + # dividing the squarefree polynomial + # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a + # single linear factor and return the root. + # + # When degree is None, we check all degrees smaller than the degree of the + # squarefree polynomial, otherwise we work with only a single degree set by + # the user. + + # Initial checks for bad input + if self.degree().is_zero(): + raise ValueError(f"there are no irreducible factors of {self}") + + # If we know the polynomial is square-free, we can start here + if assume_squarefree: + if assume_distinct_deg: + return self._cantor_zassenhaus_split_to_irreducible(degree) + return self._any_irreducible_factor_squarefree(degree) + + # Otherwise we compute the squarefree decomposition and check each + # polynomial for a root. If no poly has a root, we raise an error. + SFD = self.squarefree_decomposition() + SFD.sort() + for poly, _ in SFD: + try: + return poly._any_irreducible_factor_squarefree(degree) + except ValueError: + pass + + # If degree has been set, there could just be no factor of the desired degree + if degree: + raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") + # But if any degree is allowed then there should certainly be a factor if self has degree > 0 + raise AssertionError(f"no irreducible factor was computed for {self}. Bug.") + def any_root(self, ring=None, degree=None, assume_squarefree=False): """ Return a root of this polynomial in the given ring. @@ -2168,12 +2360,6 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: no roots (non-field) x^2 + 1 """ - # Currently I can't see a benefit of the degree method, so I think we should - # remove it - if degree is not None: - from sage.misc.superseded import deprecation - deprecation(37170, "'degree' will soon be deprecated as it is no longer needed") - # When not working over a finite field, do the simple # thing of factoring for roots and picking the first # root. If none available, raise an error. @@ -2182,137 +2368,35 @@ cdef class Polynomial(CommutativePolynomial): rs = self.roots(ring=ring, multiplicities=False) if rs: return rs[0] - raise ValueError("polynomial {self} has no roots" % self) - - # For finite fields, we find roots in the following three steps: - # - # 1. Compute the squarefree decomposition of the polynomial - # 2. For each squarefree polynomial find the distinct degree = 1 - # factorison, F, which is the product of degree one polynomials - # dividing the squarefree polynomial - # 3. Using Cantor-Zassenhaus splitting with degree one to find a - # single linear factor and return the root. - # - # These steps are performed by the following helper functions - # - # ======================== # - # Begin helper functions # - # ======================== # - def _linear_root(f, ring=None): - if ring is None: - return -f[0] / f[1] - return ring(-f[0] / f[1]) - - def _any_root_squarefree(f, ring=None): - # Compute the distinct degree factorisation for degree one, - # the output will be the product of #all degree one - # irreducible polynomials dividing f - q = f.base_ring().order() # p^k - R, X = f.parent().objgen() - w = pow(X, q, f) - X - g = f.gcd(w) - - # When the degree is one, we have found our - # root. If the degree is zero, there are no roots - if g.degree().is_one(): - return _linear_root(g, ring=ring) - elif g.degree().is_zero(): - raise ValueError(f"no root can be computed for {f}") - - # Otherwise, we can find a root from the CZ splitting of g - return _cantor_zassenhaus_split_root(g, 1, ring=ring) - - def _cantor_zassenhaus_split_root(f, degree, ring=None): - R = f.parent() - q = f.base_ring().order() - # Need to handle odd and even charcteristic separately - if q % 2 != 0: - # We expect to succeed with 1/2 probability, so if we - # try 1000 times and fail, there's a bug somewhere. - for _ in range(1000): - T = R.random_element(2*degree + 1) - if T.is_zero(): continue # skip T = 0 - T = T.monic() - - # Compute the gcd. 50% chance this is a non-trivial factor of f - h = f.gcd(pow(T, (q-1)//2, f)-1) - hd = h.degree() - - # If we found a degree-one root, return it - if hd.is_one(): - return _linear_root(h, ring=ring) - - # Else check if we have a non-trivial factor and keep going - if not hd.is_zero() and hd != f.degree(): - if 2*hd <= f.degree(): - return _cantor_zassenhaus_split_root(h, degree, ring=ring) - else: - return _cantor_zassenhaus_split_root(f//h, degree, ring=ring) - # If you are reaching this error, chances are there's a bug in the code. - raise ValueError(f"no splitting of degree {degree} found for {f}") - - # Now handle even charactertistic - for _ in range(1000): - # Sample a uniformly random element of R - T = R.random_element(2*degree + 1) - if T.is_zero(): continue - T = T.monic() - - # Compute the trace of T with field of order 2^k - # sum T^(2^i) for i in range (degree * k) - # We use repeated squaring to avoid redundent multiplications - C, TT = T, T - for _ in range(degree * f.base_ring().degree() - 1): - TT = pow(TT, 2, f) - C += TT - - # Compute the gcd to find a factor - h = f.gcd(C) - hd = h.degree() - - # If we found a degree-one root, return it - if hd.is_one(): - return _linear_root(h, ring=ring) - - # else check if we have a factor - elif not hd.is_zero() and hd != f.degree(): - if 2*hd <= f.degree(): - return _cantor_zassenhaus_split_root(h, degree, ring) - else: - return _cantor_zassenhaus_split_root(f // h, degree, ring) - - # If you are reaching this error, chances are there's a bug in the code. - raise ValueError(f"no splitting of degree {degree} found for {f}") - # ======================== # - # End helper functions # - # ======================== # - - # Initial checks for bad input - if self.degree() < 0: - if ring is None: - return self.base_ring()(0) - return ring(0) - elif self.degree() == 0: - raise ValueError(f"no root can be computed for {self}") + raise ValueError(f"polynomial {self} has no roots") - # If the input is a linear polynomial, simply compute the root - if self.degree() == 1: - return _linear_root(self, ring=ring) - - # If we know the polynomial is square-free, we can start here - if assume_squarefree: - return _any_root_squarefree(self, ring=ring) - - # Otherwise we compute the squarefree decomposition and check each - # polynomial for a root. If no poly has a root, we raise an error. - SFD = self.squarefree_decomposition() - SFD.sort() - for poly, _ in SFD: + # Look for a linear factor, if there is none, raise a ValueError + if degree is None: try: - return _any_root_squarefree(poly, ring=ring) + f = self.any_irreducible_factor(ring=ring, degree=ZZ(1), assume_squarefree=assume_squarefree) + return - f[0] / f[1] except ValueError: - pass - raise ValueError(f"no root can be computed for {self}") + raise ValueError(f"no root of polynomial {self} can be computed") + + # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial + # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and + # ensure that the degree is positive. + assume_distinct_deg = False + if degree < 0: + degree = ZZ(abs(degree)) + assume_distinct_deg = True + + # If a certain degree is requested, then we find an irreducible factor of degree `degree` + # use this to compute a field extension and return the generator as root of this polynomial + try: + f = self.any_irreducible_factor(ring=ring, degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) + except ValueError: + raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") + + if degree.is_one(): + return - f[0] / f[1] + F_ext = self.base_ring().extension(f, names="a") + return F_ext.gen() def __truediv__(left, right): r""" From bf8b656ef7a0df51c1d396e6bc3a2d17796ae8d4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 29 Jan 2024 15:37:46 +0100 Subject: [PATCH 034/191] correct and simplify argument handling --- src/sage/algebras/fusion_rings/fusion_ring.py | 4 ++-- src/sage/rings/fraction_field_element.pyx | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 91cec5ad5c7..72bc0f8600c 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1572,9 +1572,9 @@ def q_dimension(self, base_coercion=True): elif exp < 0: expr /= q_int(P._nf * val, q)**(-exp) expr = R(expr) - expr = expr.subs(q=q**4) / (q**(2*expr.degree())) + expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) - ret = expr.subs(q=zet) + ret = expr.substitute(q=zet) if (not base_coercion) or (self.parent()._basecoer is None): return ret diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index fea008d7d13..db663eb674b 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -487,6 +487,8 @@ cdef class FractionFieldElement(FieldElement): den = self._denominator.subs(*args, **kwds) return num / den + substitute = subs + def _is_atomic(self): """ EXAMPLES:: From 32066e28ea25f616b67178a6adfd190f2d04d8f8 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:40:42 +0000 Subject: [PATCH 035/191] Fix linting issues --- .../rings/polynomial/polynomial_element.pyx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 6211248c42b..aa1da8830a7 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2055,7 +2055,7 @@ cdef class Polynomial(CommutativePolynomial): Creates an iterator for all valid degrees `d`, and return tuples of the form `(a_d, d)` for a polynomial `a_d` the product of irreducible polynomials of degree `d` - + Assumes that self is squarefree. """ q = self.base_ring().order() # p^k @@ -2071,7 +2071,7 @@ cdef class Polynomial(CommutativePolynomial): e = v.degree() d = d + 1 w = pow(w, q, v) - + ad = v.gcd(w - x) if not ad.is_one(): @@ -2090,8 +2090,8 @@ cdef class Polynomial(CommutativePolynomial): """ Helper function for any_irreducible_factor which computes a factor from a polynomial of the form `self = prod g_i(x)` - with all `g_i(x)` having the same degree. Uses the Cantor - Zassenhaus splitting method. + with all `g_i(x)` having the same degree. Uses the Cantor + Zassenhaus splitting method. """ R = self.parent() q = self.base_ring().order() @@ -2100,7 +2100,7 @@ cdef class Polynomial(CommutativePolynomial): if self.degree() == degree: return self - # We expect to succeed with greater than 1/2 probability, + # We expect to succeed with greater than 1/2 probability, #so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): T = R.random_element(2*degree + 1) @@ -2119,7 +2119,7 @@ cdef class Polynomial(CommutativePolynomial): for _ in range(degree * self.base_ring().degree() - 1): TT = pow(TT, 2, self) C += TT - h = self.gcd(C) + h = self.gcd(C) hd = h.degree() @@ -2136,7 +2136,7 @@ cdef class Polynomial(CommutativePolynomial): # If you are reaching this error, chances are there's a bug in the code. raise AssertionError(f"no splitting of degree {degree} found for {self}") - + def _any_irreducible_factor_squarefree(self, degree=None): """ TODO @@ -2180,13 +2180,13 @@ cdef class Polynomial(CommutativePolynomial): degree = ZZ(degree) if degree < 1: raise ValueError(f"{degree = } must be positive") - + if assume_distinct_deg and degree is None: raise ValueError("degree must be known if distinct degree factorisation is assumed") # When not working over a finite field, do the simple thing of factoring. - # If degree is none, we return the first factor, otherwise we iterate - # through and look for a polynomial with the desired degree. + # If degree is none, we return the first factor, otherwise we iterate + # through and look for a polynomial with the desired degree. from sage.categories.finite_fields import FiniteFields if self.base_ring() not in FiniteFields(): try: @@ -2209,7 +2209,7 @@ cdef class Polynomial(CommutativePolynomial): # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a # single linear factor and return the root. # - # When degree is None, we check all degrees smaller than the degree of the + # When degree is None, we check all degrees smaller than the degree of the # squarefree polynomial, otherwise we work with only a single degree set by # the user. @@ -2232,7 +2232,7 @@ cdef class Polynomial(CommutativePolynomial): return poly._any_irreducible_factor_squarefree(degree) except ValueError: pass - + # If degree has been set, there could just be no factor of the desired degree if degree: raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") @@ -2360,8 +2360,8 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: no roots (non-field) x^2 + 1 """ - # When not working over a finite field, do the simple - # thing of factoring for roots and picking the first + # When not working over a finite field, do the simple + # thing of factoring for roots and picking the first # root. If none available, raise an error. from sage.categories.finite_fields import FiniteFields if not self.base_ring() in FiniteFields(): @@ -2369,7 +2369,7 @@ cdef class Polynomial(CommutativePolynomial): if rs: return rs[0] raise ValueError(f"polynomial {self} has no roots") - + # Look for a linear factor, if there is none, raise a ValueError if degree is None: try: From b810fcc120dd8f322e8832aed63c02436bbad45b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:44:29 +0000 Subject: [PATCH 036/191] Small edits to fix review notes --- src/sage/rings/polynomial/polynomial_element.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index aa1da8830a7..e1fafc189e6 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2108,8 +2108,8 @@ cdef class Polynomial(CommutativePolynomial): continue T = T.monic() - # Need to handle odd and even charcteristic separately - if q % 2 != 0: + # Need to handle odd and even characteristic separately + if q % 2: h = self.gcd(pow(T, (q-1)//2, self)-1) else: # Compute the trace of T with field of order 2^k @@ -2117,7 +2117,7 @@ cdef class Polynomial(CommutativePolynomial): # We use repeated squaring to avoid redundent multiplications C, TT = T, T for _ in range(degree * self.base_ring().degree() - 1): - TT = pow(TT, 2, self) + TT = TT * TT % f C += TT h = self.gcd(C) @@ -2204,7 +2204,7 @@ cdef class Polynomial(CommutativePolynomial): # # 1. Compute the squarefree decomposition of the polynomial `self` # 2. For each squarefree polynomial find the distinct degree `d` - # factorison, F, which is the product of degree `d` polynomials + # factorisation, F, which is the product of degree `d` polynomials # dividing the squarefree polynomial # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a # single linear factor and return the root. From b240bcfeda89a4a036d68e0e79dd5ffe7687605c Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:47:28 +0000 Subject: [PATCH 037/191] Add docstrings to helper functions --- src/sage/rings/polynomial/polynomial_element.pyx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e1fafc189e6..064f4bdcef1 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2139,7 +2139,17 @@ cdef class Polynomial(CommutativePolynomial): def _any_irreducible_factor_squarefree(self, degree=None): """ - TODO + Helper function for any_irreducible_factor which computes + an irreducible factor from self, assuming the input is + squarefree. + + Does this by first computing the distinct degree factorisations + of self and thenfinds a factor with Cantor-Zassenhaus + splitting. + + If degree is not None, then only irreducible factors of degree + `degree` are searched for, otherwise the smallest degree factor + is found. """ # If the degree is not None we only want to check a single polynomial if degree is not None: @@ -2165,7 +2175,7 @@ cdef class Polynomial(CommutativePolynomial): def any_irreducible_factor(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ - TODO + TODO: description with doctests and tests. """ # If the ring is not None, then first change ring and run the algorithm again if ring is not None: From f26534ebcbb6f772d3652558db52027b2916e5bc Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:49:31 +0000 Subject: [PATCH 038/191] Remove the check for the zero polynomial --- src/sage/rings/polynomial/polynomial_element.pyx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 064f4bdcef1..b80a417a4b4 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2103,10 +2103,8 @@ cdef class Polynomial(CommutativePolynomial): # We expect to succeed with greater than 1/2 probability, #so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): - T = R.random_element(2*degree + 1) - if T.is_zero(): - continue - T = T.monic() + # Sample a polynomial uniformly from R + T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately if q % 2: From 66e1ae05d41c622a25743c76dca530c68676e93a Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:50:14 +0000 Subject: [PATCH 039/191] Too much whitespace --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index b80a417a4b4..90b779400b1 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2138,9 +2138,9 @@ cdef class Polynomial(CommutativePolynomial): def _any_irreducible_factor_squarefree(self, degree=None): """ Helper function for any_irreducible_factor which computes - an irreducible factor from self, assuming the input is + an irreducible factor from self, assuming the input is squarefree. - + Does this by first computing the distinct degree factorisations of self and thenfinds a factor with Cantor-Zassenhaus splitting. From 24c277948d8ffdd79730eae588802fe5f15cdd8b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 15:22:30 +0000 Subject: [PATCH 040/191] Fix refactoring bugs --- src/sage/rings/polynomial/polynomial_element.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 90b779400b1..5e9dbd63c01 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2062,26 +2062,26 @@ cdef class Polynomial(CommutativePolynomial): R, x = self.parent().objgen() # Initialise values - d = 0 v = self w = x + d = 0 + e = v.degree() # Iterate over all possible degrees with degree - while e <= 2*(d + 1): - e = v.degree() + while 2*(d + 1) <= e: d = d + 1 w = pow(w, q, v) ad = v.gcd(w - x) + yield (ad, d) if not ad.is_one(): v = v // ad w = w % v - yield (ad, d) + e = v.degree() # Last case, v itself might be irreducible - e = v.degree() if e > 0: yield (v, e) return @@ -2115,7 +2115,7 @@ cdef class Polynomial(CommutativePolynomial): # We use repeated squaring to avoid redundent multiplications C, TT = T, T for _ in range(degree * self.base_ring().degree() - 1): - TT = TT * TT % f + TT = TT * TT % self C += TT h = self.gcd(C) From 23ef18a39f68034fb261c60066c3b7f44688c313 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 29 Jan 2024 17:01:37 +0100 Subject: [PATCH 041/191] remove useless INPUT and OUTPUT blocks, fix docstring details --- src/sage/rings/fraction_field_element.pyx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index db663eb674b..5f3d122b1bd 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -448,22 +448,11 @@ cdef class FractionFieldElement(FieldElement): def subs(self, *args, **kwds): """ - Substitute variables in numerator and denominator. + Substitute variables in the numerator and denominator of ``self``. All the arguments are transmitted unchanged to the method ``subs`` of the numerator and the denominator. - INPUT: - - - ``args`` - arguments to be passed to the numerator and - denominator - - ``kwargs`` - keyword arguments to be passed to the - numerator and denominator - - OUTPUT: - - - new object if substitution is possible, otherwise self. - EXAMPLES:: sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() From f7e157c77a17537164d9c2bb376958c266a95ac5 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 17:33:17 +0000 Subject: [PATCH 042/191] Try and make new function work like th eold one --- .../rings/polynomial/polynomial_element.pyx | 83 ++++++++++++------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 5e9dbd63c01..27f5e9a4fce 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2171,18 +2171,10 @@ cdef class Polynomial(CommutativePolynomial): raise ValueError(f"no irreducible factor could be computed from {self}") - def any_irreducible_factor(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): + def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ TODO: description with doctests and tests. """ - # If the ring is not None, then first change ring and run the algorithm again - if ring is not None: - try: - f = self.change_ring(ring) - return f.any_irreducible_factor(None, degree, assume_squarefree, assume_distinct_deg) - except (TypeError, ValueError): - raise TypeError(f"unable to coerce from {self.base_ring()} to {ring}") - # Make sure the user inputted something reasonable for degree if degree is not None: degree = ZZ(degree) @@ -2277,26 +2269,30 @@ cdef class Polynomial(CommutativePolynomial): sage: f.factor() (7) * (x + 9) * (x^6 + 10*x^4 + 6*x^3 + 5*x^2 + 2*x + 2) sage: f = x^6 + 10*x^4 + 6*x^3 + 5*x^2 + 2*x + 2 - sage: f.any_root(GF(11^6, 'a')) - a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a - sage: sorted(f.roots(GF(11^6, 'a'))) - [(10*a^5 + 2*a^4 + 8*a^3 + 9*a^2 + a, 1), - (a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a, 1), - (9*a^5 + 5*a^4 + 10*a^3 + 8*a^2 + 3*a + 1, 1), - (2*a^5 + 8*a^4 + 3*a^3 + 6*a + 2, 1), - (a^5 + 3*a^4 + 8*a^3 + 2*a^2 + 3*a + 4, 1), - (10*a^5 + 3*a^4 + 8*a^3 + a^2 + 10*a + 4, 1)] - sage: f.any_root(GF(11^6, 'a')) - a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a + sage: root = f.any_root(GF(11^6, 'a')) + sage: roots = sorted(f.roots(GF(11^6, 'a'), multiplicities=False)) + sage: roots + [10*a^5 + 2*a^4 + 8*a^3 + 9*a^2 + a, + a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a, + 9*a^5 + 5*a^4 + 10*a^3 + 8*a^2 + 3*a + 1, + 2*a^5 + 8*a^4 + 3*a^3 + 6*a + 2, + a^5 + 3*a^4 + 8*a^3 + 2*a^2 + 3*a + 4, + 10*a^5 + 3*a^4 + 8*a^3 + a^2 + 10*a + 4] + sage: root in roots + True sage: # needs sage.rings.finite_rings - sage: g = (x-1)*(x^2 + 3*x + 9) * (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5) + sage: g = (x-1) * (x^2 + 3*x + 9) * (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5) sage: g.any_root(ring=GF(11^10, 'b'), degree=1) 1 - sage: g.any_root(ring=GF(11^10, 'b'), degree=2) - 5*b^9 + 4*b^7 + 4*b^6 + 8*b^5 + 10*b^2 + 10*b + 5 - sage: g.any_root(ring=GF(11^10, 'b'), degree=5) - 5*b^9 + b^8 + 3*b^7 + 2*b^6 + b^5 + 4*b^4 + 3*b^3 + 7*b^2 + 10*b + sage: root = g.any_root(ring=GF(11^10, 'b'), degree=2) + sage: roots = (x^2 + 3*x + 9).roots(ring=GF(11^10, 'b'), multiplicities=False) + sage: root in roots + True + sage: root = g.any_root(ring=GF(11^10, 'b'), degree=5) + sage: roots = (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5).roots(ring=GF(11^10, 'b'), multiplicities=False) + sage: root in roots + True TESTS:: @@ -2366,7 +2362,7 @@ cdef class Polynomial(CommutativePolynomial): sage: (x^2 + 1).any_root() Traceback (most recent call last): ... - ValueError: no roots (non-field) x^2 + 1 + ValueError: polynomial x^2 + 1 has no roots """ # When not working over a finite field, do the simple # thing of factoring for roots and picking the first @@ -2380,12 +2376,21 @@ cdef class Polynomial(CommutativePolynomial): # Look for a linear factor, if there is none, raise a ValueError if degree is None: + # if a ring is given try and coerce the polynomial into this ring + if ring is not None: + try: + self = self.change_ring(ring) + except ValueError: + raise(f"cannot coerce polynomial {self} to the new ring: {ring}") + + # try and find a linear irreducible polynomial from f to compute a root try: - f = self.any_irreducible_factor(ring=ring, degree=ZZ(1), assume_squarefree=assume_squarefree) - return - f[0] / f[1] + f = self.any_irreducible_factor(degree=ZZ(1), assume_squarefree=assume_squarefree) except ValueError: raise ValueError(f"no root of polynomial {self} can be computed") + return - f[0] / f[1] + # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and # ensure that the degree is positive. @@ -2396,13 +2401,31 @@ cdef class Polynomial(CommutativePolynomial): # If a certain degree is requested, then we find an irreducible factor of degree `degree` # use this to compute a field extension and return the generator as root of this polynomial + # if the degree and a ring is given however, instead compute a degree `degree` factor in the + # base ring and then find a factor from this in the supplied ring. try: - f = self.any_irreducible_factor(ring=ring, degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) + f = self.any_irreducible_factor(degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) except ValueError: raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") + # For the case when the degree is one, just return the root if degree.is_one(): - return - f[0] / f[1] + root = - f[0] / f[1] + if ring is None: + return root + return ring(root) + + # If the user asks for a specific ring, find the root in this ring from + # the polynomial of user supplied degree `degree` + if ring is not None: + try: + f = f.change_ring(ring) + except ValueError: + raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") + return f.any_root() + + # Otherwise change the ring of this degree `degree` irreducible + # polynomial and attempt to find a root from this F_ext = self.base_ring().extension(f, names="a") return F_ext.gen() From afa5f5f6e4b171fdf41621a4f060a4a0089a86a2 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 17:40:38 +0000 Subject: [PATCH 043/191] white space --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 27f5e9a4fce..dfb70ce742c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2382,7 +2382,7 @@ cdef class Polynomial(CommutativePolynomial): self = self.change_ring(ring) except ValueError: raise(f"cannot coerce polynomial {self} to the new ring: {ring}") - + # try and find a linear irreducible polynomial from f to compute a root try: f = self.any_irreducible_factor(degree=ZZ(1), assume_squarefree=assume_squarefree) @@ -2424,7 +2424,7 @@ cdef class Polynomial(CommutativePolynomial): raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") return f.any_root() - # Otherwise change the ring of this degree `degree` irreducible + # Otherwise change the ring of this degree `degree` irreducible # polynomial and attempt to find a root from this F_ext = self.base_ring().extension(f, names="a") return F_ext.gen() From aa5a29060d0c6cf6b164ea3d613ce58939018d14 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 21:29:10 +0000 Subject: [PATCH 044/191] Replace trac with issue throughout polynomial_element.pyx --- .../rings/polynomial/polynomial_element.pyx | 178 +++++++++--------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index dfb70ce742c..7c66eecce48 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -43,7 +43,7 @@ AUTHORS: - David Zureick-Brown (2017-09): added is_weil_polynomial - Sebastian Oehms (2018-10): made :meth:`roots` and :meth:`factor` work over more - cases of proper integral domains (see :trac:`26421`) + cases of proper integral domains (see :issue:`26421`) """ @@ -613,7 +613,7 @@ cdef class Polynomial(CommutativePolynomial): sage: F(y=1) x*t - The following shows that :trac:`2360` is indeed fixed. :: + The following shows that :issue:`2360` is indeed fixed. :: sage: R. = ZZ[] sage: P. = ZZ[] @@ -628,13 +628,13 @@ cdef class Polynomial(CommutativePolynomial): sage: f(x) 6*x^4 - The following shows that :trac:`9006` is also fixed. :: + The following shows that :issue:`9006` is also fixed. :: sage: f = ZZ['x'](1000000 * [1]) sage: f(1) 1000000 - The following test came up in :trac:`9051`:: + The following test came up in :issue:`9051`:: sage: # needs sage.rings.complex_interval_field sage: Cif = ComplexIntervalField(64) @@ -644,7 +644,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f(jj).center(), f(jj).diameter() (1.00000000000000000, 4.00000000000000000) - The following failed before the patch to :trac:`3979` + The following failed before the patch to :issue:`3979` :: @@ -700,7 +700,7 @@ cdef class Polynomial(CommutativePolynomial): ... TypeError: Wrong number of arguments - Check that :trac:`22317` is fixed:: + Check that :issue:`22317` is fixed:: sage: R = ZZ['x']['y']['z'] sage: d = R.gens_dict_recursive() @@ -708,7 +708,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p(x=QQ(0)) 0 - Check that :trac:`32513` is fixed:: + Check that :issue:`32513` is fixed:: sage: R. = PolynomialRing(ZZ) sage: x.substitute([]) @@ -716,7 +716,7 @@ cdef class Polynomial(CommutativePolynomial): sage: Polynomial.__call__(x, []) x - These were drastically slower prior to :trac:`33165`:: + These were drastically slower prior to :issue:`33165`:: sage: # needs sage.rings.finite_rings sage: R. = GF(31337)[] @@ -754,7 +754,7 @@ cdef class Polynomial(CommutativePolynomial): sage: g(U(2)) -8268 - Sparse tests for :trac:`33165`:: + Sparse tests for :issue:`33165`:: sage: R. = PolynomialRing(QQ, sparse=True) sage: f = x^1000000 + 1 @@ -1030,7 +1030,7 @@ cdef class Polynomial(CommutativePolynomial): sage: x^3 - 3 > 393939393 True - Test comparison with zero (:trac:`18633`):: + Test comparison with zero (:issue:`18633`):: sage: 0 < R(1) True @@ -1150,7 +1150,7 @@ cdef class Polynomial(CommutativePolynomial): sage: pol[:6] 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x - Any other kind of slicing is an error, see :trac:`18940`:: + Any other kind of slicing is an error, see :issue:`18940`:: sage: f[1:3] Traceback (most recent call last): @@ -1261,7 +1261,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Verify that :trac:`16251` has been resolved, i.e., polynomials with + Verify that :issue:`16251` has been resolved, i.e., polynomials with unhashable coefficients are unhashable:: sage: K. = Qq(9) # needs sage.rings.padics @@ -1495,7 +1495,7 @@ cdef class Polynomial(CommutativePolynomial): 10 The polynomial has to be over a field of characteristic 0 (see - :trac:`24072`):: + :issue:`24072`):: sage: R. = GF(7)[] sage: f = SR(2*w^3 + 1); f # needs sage.symbolic @@ -2012,7 +2012,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Make sure :trac:`9093` is fixed:: + Make sure :issue:`9093` is fixed:: sage: R(1).is_square() True @@ -2023,7 +2023,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R(0).is_square() True - Make sure :trac:`35860` is fixed:: + Make sure :issue:`35860` is fixed:: sage: S. = PolynomialRing(ZZ) sage: is_square(S(1), True)[1].parent() @@ -2303,7 +2303,7 @@ cdef class Polynomial(CommutativePolynomial): ....: assert f(f.any_root(K)) == 0 Check that our Cantor-Zassenhaus implementation does not loop - over finite fields of even characteristic (see :trac:`16162`):: + over finite fields of even characteristic (see :issue:`16162`):: sage: # needs sage.rings.finite_rings sage: K. = GF(2**8) @@ -2346,7 +2346,7 @@ cdef class Polynomial(CommutativePolynomial): + a^27 + a^25 + a^23 + a^22 + a^20 + a^18 + a^16 + a^14 + a^11 + a^10 + a^8 + a^6 + a^5 + a^4 + a + 1 - Check that :trac:`21998` has been resolved:: + Check that :issue:`21998` has been resolved:: sage: # needs sage.rings.finite_rings sage: K. = GF(2^4) @@ -2481,7 +2481,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`12217` is fixed:: + Check that :issue:`12217` is fixed:: sage: P. = GF(5)[] sage: x/0 @@ -2495,7 +2495,7 @@ cdef class Polynomial(CommutativePolynomial): ... ZeroDivisionError: division by zero in finite field - Check that :trac:`23611` is fixed:: + Check that :issue:`23611` is fixed:: sage: int(1) / x 1/x @@ -2572,7 +2572,7 @@ cdef class Polynomial(CommutativePolynomial): sage: pow(f, 10**7, h) 4*x*t^3 + 2*x*t^2 + 4*x*t + 4 - Check that :trac:`18457` is fixed:: + Check that :issue:`18457` is fixed:: sage: R. = PolynomialRing(GF(5), sparse=True) sage: (1+x)^(5^10) @@ -2610,7 +2610,7 @@ cdef class Polynomial(CommutativePolynomial): 18009460*y^6*x^6 + 2349060*y^5*x^5 + ... + 51*y*x + 1 Check that fallback method is used when it is not possible to compute - the characteristic of the base ring (:trac:`24308`):: + the characteristic of the base ring (:issue:`24308`):: sage: # needs sage.libs.singular sage: kk. = GF(2)[] @@ -2805,7 +2805,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - We verify that :trac:`23020` has been resolved. (There are no + We verify that :issue:`23020` has been resolved. (There are no elements in the Sage library yet that do not implement ``__bool__``, so we have to create one artificially.):: @@ -2898,7 +2898,7 @@ cdef class Polynomial(CommutativePolynomial): sage: latex(x+2) x + 2.0 - The following illustrates the fix of :trac:`2586`:: + The following illustrates the fix of :issue:`2586`:: sage: latex(ZZ['alpha']['b']([0, ZZ['alpha'].0])) \alpha b @@ -3085,7 +3085,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check the problem reported at :trac:`12529` is fixed:: + Check the problem reported at :issue:`12529` is fixed:: sage: # needs sage.rings.finite_rings sage: gens = 'y a0 a1 a2 b0 b1 b2 c1 c2 d0 d1 d2 d3 d4 d5 d6 d7'.split() @@ -3554,7 +3554,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`25022` is fixed:: + Check that :issue:`25022` is fixed:: sage: K. = ZZ[] sage: x.change_ring(SR) == SR['x'].gen() # needs sage.symbolic @@ -3562,7 +3562,7 @@ cdef class Polynomial(CommutativePolynomial): sage: x.change_ring(ZZ['x']) == ZZ['x']['x'].gen() True - Check that :trac:`28541` is fixed:: + Check that :issue:`28541` is fixed:: sage: # needs sage.rings.finite_rings sage: F. = GF(7^2) @@ -3766,7 +3766,7 @@ cdef class Polynomial(CommutativePolynomial): 1.00000000000000 Check that the denominator is an element over the base whenever the base - has no :meth:`denominator` method. This closes :trac:`9063`. :: + has no :meth:`denominator` method. This closes :issue:`9063`. :: sage: R. = GF(5)[] sage: x = R(0) @@ -3781,7 +3781,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18518` is fixed:: + Check that :issue:`18518` is fixed:: sage: R. = PolynomialRing(QQ, sparse=True) sage: p = x^(2^100) - 1/2 @@ -3974,7 +3974,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R(1).derivative(R(x)) 0 - Check that :trac:`28147` is fixed:: + Check that :issue:`28147` is fixed:: sage: # needs sage.rings.finite_rings sage: R. = GF(65537)[] @@ -4011,7 +4011,7 @@ cdef class Polynomial(CommutativePolynomial): ValueError: cannot differentiate with respect to y - Check that :trac:`26844` is fixed by :trac:`28147`:: + Check that :issue:`26844` is fixed by :issue:`28147`:: sage: A = PolynomialRing(GF(3), name='t') sage: K = A.fraction_field() @@ -4019,7 +4019,7 @@ cdef class Polynomial(CommutativePolynomial): sage: t.derivative(t) 1 - Check that :trac:`28187` is fixed:: + Check that :issue:`28187` is fixed:: sage: R. = GF(65537)[] # needs sage.rings.finite_rings sage: x._derivative(2*x) @@ -4104,7 +4104,7 @@ cdef class Polynomial(CommutativePolynomial): sage: g.parent() Univariate Polynomial Ring in x over Rational Field - This shows that the issue at :trac:`7711` is resolved:: + This shows that the issue at :issue:`7711` is resolved:: sage: # needs sage.rings.finite_rings sage: P. = PolynomialRing(GF(2147483647)) @@ -4155,7 +4155,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: Sx. = ZZ[] sage: Sxy. = Sx[] @@ -4176,7 +4176,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.integral(x).derivative(x) == p True - Check that it works with non-integral domains (:trac:`18600`):: + Check that it works with non-integral domains (:issue:`18600`):: sage: x = polygen(Zmod(4)) sage: p = x**4 + 1 @@ -4407,7 +4407,7 @@ cdef class Polynomial(CommutativePolynomial): rings with composite characteristic is not implemented Factoring polynomials over the algebraic numbers (see - :trac:`8544`):: + :issue:`8544`):: sage: R. = QQbar[] # needs sage.rings.number_field sage: (x^8 - 1).factor() # needs sage.rings.number_field @@ -4417,7 +4417,7 @@ cdef class Polynomial(CommutativePolynomial): * (x + 0.7071067811865475? + 0.7071067811865475?*I) * (x + 1) Factoring polynomials over the algebraic reals (see - :trac:`8544`):: + :issue:`8544`):: sage: R. = AA[] # needs sage.rings.number_field sage: (x^8 + 1).factor() # needs sage.rings.number_field @@ -4428,7 +4428,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - This came up in :trac:`7088`:: + This came up in :issue:`7088`:: sage: R. = PolynomialRing(ZZ) sage: f = 12*x^10 + x^9 + 432*x^3 + 9011 @@ -4439,7 +4439,7 @@ cdef class Polynomial(CommutativePolynomial): sage: F = f^2 * g^3 * 7; F.factor() # needs sage.libs.pari 7 * (12*x^10 + x^9 + 432*x^3 + 9011)^2 * (13*x^11 + 89*x^3 + 1)^3 - This example came up in :trac:`7097`:: + This example came up in :issue:`7097`:: sage: # needs sage.rings.number_field sage: x = polygen(QQ) @@ -4531,7 +4531,7 @@ cdef class Polynomial(CommutativePolynomial): sage: A(x^2 - 1/3).factor() (T - a) * (T + a) - Test that :trac:`10279` is fixed:: + Test that :issue:`10279` is fixed:: sage: # needs sage.rings.number_field sage: R. = PolynomialRing(QQ) @@ -4550,7 +4550,7 @@ cdef class Polynomial(CommutativePolynomial): ... sage: pari.default("debug", 0) - Test that :trac:`10369` is fixed:: + Test that :issue:`10369` is fixed:: sage: # needs sage.rings.number_field sage: x = polygen(QQ) @@ -4634,7 +4634,7 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(f) (x - a1) * (x^2 + a1*x + a1^2) - We check that :trac:`7554` is fixed:: + We check that :issue:`7554` is fixed:: sage: L. = LaurentPolynomialRing(QQ) sage: F = L.fraction_field() @@ -4652,14 +4652,14 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(p) # needs sage.libs.singular (a/(a + 2)) * (x - a) * (b*x + c)^2 - Check that :trac:`24973` is fixed:: + Check that :issue:`24973` is fixed:: sage: x1 = ZZ['x'].gen() sage: x2 = ZZ['x']['x'].gen() sage: (x1 - x2).factor() # needs sage.libs.singular -x + x - Check that :trac:`26421' is fixed:: + Check that :issue:`26421' is fixed:: sage: R. = LaurentPolynomialRing(ZZ) sage: P. = R[] @@ -4667,7 +4667,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.factor() # needs sage.libs.singular (x - 5) * (x - 2*t) * (x^2 - 2) - Check that :trac:`29266` is fixed: + Check that :issue:`29266` is fixed: sage: f = t*x + t sage: f.is_irreducible() # needs sage.libs.singular @@ -5219,7 +5219,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`32033` has been fixed:: + Check that :issue:`32033` has been fixed:: sage: R. = GF(3)[] sage: lcm(R(0), R(0)) @@ -5833,7 +5833,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: c = x^2^100 + 1 @@ -5878,7 +5878,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(Zmod(4), sparse=True) sage: (2*x^2^100 + 2).is_nilpotent() @@ -6359,7 +6359,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^2^100 + x^8 - 1).padded_list(10) @@ -7000,7 +7000,7 @@ cdef class Polynomial(CommutativePolynomial): sage: h.parent() is R # needs sage.libs.pari sage.modules True - Check that :trac:`13672` is fixed:: + Check that :issue:`13672` is fixed:: sage: R. = GF(2)[] sage: S. = R[] @@ -7009,7 +7009,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.resultant(g) # needs sage.libs.pari t^4 + t - Check that :trac:`15061` is fixed:: + Check that :issue:`15061` is fixed:: sage: R. = PowerSeriesRing(QQ) sage: F = R([1,1],2) @@ -7018,7 +7018,7 @@ cdef class Polynomial(CommutativePolynomial): sage: P.resultant(P.derivative()) # needs sage.libs.pari -4 - 4*T + O(T^2) - Check that :trac:`16360` is fixed:: + Check that :issue:`16360` is fixed:: sage: K. = FunctionField(QQ) sage: R. = K[] @@ -7035,7 +7035,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.resultant(g) # needs sage.libs.pari 0 - Check that :trac:`17817` is fixed:: + Check that :issue:`17817` is fixed:: sage: A. = Frac(PolynomialRing(QQ,'a,b,c')) sage: B. = PolynomialRing(A,'d,e,f') @@ -7049,7 +7049,7 @@ cdef class Polynomial(CommutativePolynomial): + (48*c^4)*e^2*x^2 + ((-12*a*c)/b*d^2*e + (-12*b*c)*e)*x + (16*c^4)*e^3 + (4*a^3*b^3)/c - Test for :trac:`10978`:: + Test for :issue:`10978`:: sage: # needs sage.libs.pari sage.rings.complex_double sage.symbolic sage: R. = PolynomialRing(CDF) @@ -7658,7 +7658,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.discriminant() # needs sage.libs.pari 1 - Check that :trac:`13672` is fixed:: + Check that :issue:`13672` is fixed:: sage: R. = GF(5)[] sage: S. = R[] @@ -7666,7 +7666,7 @@ cdef class Polynomial(CommutativePolynomial): sage: (f - t).discriminant() # needs sage.rings.finite_rings 4*t^5 - The following examples show that :trac:`11782` has been fixed:: + The following examples show that :issue:`11782` has been fixed:: sage: var('x') # needs sage.symbolic x @@ -7675,13 +7675,13 @@ cdef class Polynomial(CommutativePolynomial): sage: ZZ.quo(9)['x'](2*x^3 + x^2 + x).discriminant() # needs sage.libs.pari sage.symbolic 2 - This was fixed by :trac:`15422`:: + This was fixed by :issue:`15422`:: sage: R. = PolynomialRing(Qp(2)) # needs sage.rings.padics sage: (s^2).discriminant() # needs sage.rings.padics 0 - This was fixed by :trac:`16014`:: + This was fixed by :issue:`16014`:: sage: # needs sage.modules sage: PR. = QQ[] @@ -7693,7 +7693,7 @@ cdef class Polynomial(CommutativePolynomial): sage: det(mu*E1 + E2).discriminant().degrees() # needs sage.libs.pari (24, 12, 12, 8, 8, 8, 8) - This addresses an issue raised by :trac:`15061`:: + This addresses an issue raised by :issue:`15061`:: sage: R. = PowerSeriesRing(QQ) sage: F = R([1,1],2) @@ -7976,7 +7976,7 @@ cdef class Polynomial(CommutativePolynomial): [] For some other polynomials, no roots can be found at the moment - due to the way roots are computed. :trac:`17516` addresses + due to the way roots are computed. :issue:`17516` addresses these defects. Until that gets implemented, one such example is the following:: @@ -8291,12 +8291,12 @@ cdef class Polynomial(CommutativePolynomial): sage: p.roots(ring=QQbar) # needs sage.rings.number_field [(-5.9223865215328558?e225, 1), (5.9223865215328558?e225, 1)] - Check that :trac:`30522` is fixed:: + Check that :issue:`30522` is fixed:: sage: PolynomialRing(SR, names="x")("x^2").roots() # needs sage.symbolic [(0, 2)] - Check that :trac:`30523` is fixed:: + Check that :issue:`30523` is fixed:: sage: PolynomialRing(SR, names="x")("x^2 + q").roots() # needs sage.symbolic [(-sqrt(-q), 1), (sqrt(-q), 1)] @@ -8329,7 +8329,7 @@ cdef class Polynomial(CommutativePolynomial): :meth:`sage.symbolic.expression.Expression.solve` which in turn uses Maxima to find radical solutions. Some solutions may be lost in this approach. - Once :trac:`17516` gets implemented, all possible radical + Once :issue:`17516` gets implemented, all possible radical solutions should become available. If `L` is ``AA`` or ``RIF``, and `K` is ``ZZ``, ``QQ``, or ``AA``, then the root isolation @@ -8396,7 +8396,7 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(x^3 - 1) (x - 1) * (x^2 + x + 1) - This shows that the issue from :trac:`6237` is fixed:: + This shows that the issue from :issue:`6237` is fixed:: sage: R. = QQ[] sage: g = -27*u^14 - 32*u^9 @@ -8407,7 +8407,7 @@ cdef class Polynomial(CommutativePolynomial): [(-1.0345637159435719, 1), (0.0, 9), (-0.3196977699902601 - 0.9839285635706636*I, 1), (-0.3196977699902601 + 0.9839285635706636*I, 1), (0.8369796279620465 - 0.6081012947885318*I, 1), (0.8369796279620465 + 0.6081012947885318*I, 1)] - This shows that the issue at :trac:`2418` is fixed:: + This shows that the issue at :issue:`2418` is fixed:: sage: x = polygen(QQ) sage: p = (x^50/2^100 + x^10 + x + 1).change_ring(ComplexField(106)) # needs sage.rings.real_mpfr @@ -8416,7 +8416,7 @@ cdef class Polynomial(CommutativePolynomial): sage: [abs(p(rt)) < eps for rt in rts] == [True]*50 # needs sage.rings.number_field True - This shows that the issue at :trac:`10901` is fixed:: + This shows that the issue at :issue:`10901` is fixed:: sage: # needs sage.symbolic sage: a = var('a'); R. = SR[] @@ -8431,7 +8431,7 @@ cdef class Polynomial(CommutativePolynomial): TypeError: cannot evaluate symbolic expression to a numeric value We can find roots of polynomials defined over `\ZZ` or `\QQ` - over the `p`-adics, see :trac:`15422`:: + over the `p`-adics, see :issue:`15422`:: sage: R. = ZZ[] sage: pol = (x - 1)^2 @@ -8450,14 +8450,14 @@ cdef class Polynomial(CommutativePolynomial): sage: parent(r[0]) 3-adic Ring with capped relative precision 5 - Spurious crash with pari-2.5.5, see :trac:`16165`:: + Spurious crash with pari-2.5.5, see :issue:`16165`:: sage: f = (1+x+x^2)^3 sage: f.roots(ring=CC) # needs sage.libs.pari sage.rings.real_mpfr [(-0.500000000000000 - 0.866025403784439*I, 3), (-0.500000000000000 + 0.866025403784439*I, 3)] - Test a crash reported at :trac:`19649`:: + Test a crash reported at :issue:`19649`:: sage: polRing. = PolynomialRing(ZZ) sage: j = (x+1)^2 * (x-1)^7 * (x^2-x+1)^5 @@ -8467,19 +8467,19 @@ cdef class Polynomial(CommutativePolynomial): (0.500000000000000 - 0.866025403784439*I, 5), (0.500000000000000 + 0.866025403784439*I, 5)] - Test that some large finite rings can be handled (:trac:`13825`):: + Test that some large finite rings can be handled (:issue:`13825`):: sage: R. = IntegerModRing(20000009)[] # needs sage.libs.pari sage: eq = x^6+x-17 sage: eq.roots(multiplicities=False) # needs sage.libs.pari [3109038, 17207405] - Test that roots in fixed modulus p-adic fields work (:trac:`17598`):: + Test that roots in fixed modulus p-adic fields work (:issue:`17598`):: sage: len(cyclotomic_polynomial(3).roots(ZpFM(739, 566))) # needs sage.rings.padics 2 - Check that :trac:`26421` is fixed:: + Check that :issue:`26421` is fixed:: sage: R. = LaurentPolynomialRing(ZZ) sage: P. = R[] @@ -8487,7 +8487,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.roots() # needs sage.libs.singular [(5, 1), (2*t, 1)] - Check that :trac:`31040` is fixed:: + Check that :issue:`31040` is fixed:: sage: R. = QQ[] sage: K. = Qq(3).extension(x^2 + 1) # needs sage.rings.padics @@ -8497,14 +8497,14 @@ cdef class Polynomial(CommutativePolynomial): + 2*a*3^11 + 2*a*3^12 + 2*a*3^13 + 2*a*3^14 + 2*a*3^15 + 2*a*3^16 + 2*a*3^17 + 2*a*3^18 + 2*a*3^19 + O(3^20), 1)] - Check that :trac:`31710` is fixed:: + Check that :issue:`31710` is fixed:: sage: CBF['x'].zero().roots(multiplicities=False) # needs sage.libs.flint Traceback (most recent call last): ... ArithmeticError: taking the roots of the zero polynomial - Check that :trac:`33979` is fixed:: + Check that :issue:`33979` is fixed:: sage: # needs sage.libs.pari sage: n = randint(2, 10^6) @@ -9175,7 +9175,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`28395` is fixed:: + Check that :issue:`28395` is fixed:: sage: P. = QQ[] sage: u = t^4 + 3*t^2 + 1 @@ -9254,7 +9254,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`28395` is fixed:: + Check that :issue:`28395` is fixed:: sage: P. = QQ[] sage: u = t^10 + 4*t^9 + 8*t^8 + 18*t^7 + 81*t^6 + 272*t^5 + 567*t^4 + 882*t^3 + 2744*t^2 + 9604*t + 16807 @@ -10133,7 +10133,7 @@ cdef class Polynomial(CommutativePolynomial): sage: radical(12 * x^5) 6*x - If self has a factor of multiplicity divisible by the characteristic (see :trac:`8736`):: + If self has a factor of multiplicity divisible by the characteristic (see :issue:`8736`):: sage: P. = GF(2)[] sage: (x^3 + x^2).radical() # needs sage.rings.finite_rings @@ -10225,7 +10225,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.norm(int(2)) 2.00000000000000 - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^2^100 + 1).norm(1) # needs sage.rings.real_mpfr @@ -11856,7 +11856,7 @@ cdef class Polynomial_generic_dense(Polynomial): """ TESTS: - Check that exceptions are propagated correctly (:trac:`18274`):: + Check that exceptions are propagated correctly (:issue:`18274`):: sage: class BrokenRational(Rational): ....: def __bool__(self): @@ -11953,7 +11953,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that :trac:`13048` and :trac:`2034` are fixed:: + Check that :issue:`13048` and :issue:`2034` are fixed:: sage: # needs sage.rings.number_field sage: R. = QQbar[] @@ -12101,7 +12101,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that :trac:`12552` is fixed:: + Check that :issue:`12552` is fixed:: sage: type(f.degree()) @@ -12184,7 +12184,7 @@ cdef class Polynomial_generic_dense(Polynomial): ZeroDivisionError: division by zero polynomial Polynomials over noncommutative rings are also allowed - (after :trac:`34733`):: + (after :issue:`34733`):: sage: # needs sage.combinat sage.modules sage: HH = QuaternionAlgebra(QQ, -1, -1) @@ -12197,7 +12197,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - The following shows that :trac:`16649` is indeed fixed. :: + The following shows that :issue:`16649` is indeed fixed. :: sage: P. = QQ[] sage: R. = P[] @@ -12211,7 +12211,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: h == q*f + r and r.degree() < f.degree() True - :trac:`26907`:: + :issue:`26907`:: sage: P. = ZZ[] sage: R. = P[] @@ -12363,7 +12363,7 @@ cpdef Polynomial generic_power_trunc(Polynomial p, Integer n, long prec) noexcep sage: from sage.rings.polynomial.polynomial_element import generic_power_trunc - sage: for S in [ZZ, GF(3)]: # known bug, not tested (see :trac:`32075`) + sage: for S in [ZZ, GF(3)]: # known bug, not tested (see :issue:`32075`) ....: R = PolynomialRing(S, 'x') ....: for _ in range(100): ....: p = R.random_element() @@ -12579,7 +12579,7 @@ cdef class ConstantPolynomialSection(Map): """ This class is used for conversion from a polynomial ring to its base ring. - Since :trac:`9944`, it calls the :meth:`constant_coefficient` method, + Since :issue:`9944`, it calls the :meth:`constant_coefficient` method, which can be optimized for a particular polynomial type. EXAMPLES:: @@ -12646,7 +12646,7 @@ cdef class PolynomialBaseringInjection(Morphism): We demonstrate that most polynomial ring classes use polynomial base injection maps for coercion. They are supposed to be the fastest maps for that purpose. See - :trac:`9944`. :: + :issue:`9944`. :: sage: # needs sage.rings.padics sage: R. = Qp(3)[] @@ -12673,7 +12673,7 @@ cdef class PolynomialBaseringInjection(Morphism): From: Rational Field To: Univariate Polynomial Ring in x over Rational Field - By :trac:`9944`, there are now only very few exceptions:: + By :issue:`9944`, there are now only very few exceptions:: sage: PolynomialRing(QQ,names=[]).coerce_map_from(QQ) Call morphism: @@ -12806,7 +12806,7 @@ cdef class PolynomialBaseringInjection(Morphism): sage: S.coerce_map_from(R).is_injective() True - Check that :trac:`23203` has been resolved:: + Check that :issue:`23203` has been resolved:: sage: R.is_subring(S) # indirect doctest True From 006fbae2bc6e8d8eb03b3ebceac9ca7789f2d3f6 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 22:12:30 +0000 Subject: [PATCH 045/191] Add doctests for new functions --- .../rings/polynomial/polynomial_element.pyx | 179 +++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 7c66eecce48..a1c5745061c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2057,6 +2057,29 @@ cdef class Polynomial(CommutativePolynomial): product of irreducible polynomials of degree `d` Assumes that self is squarefree. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x + 162) * (x^3 + 7*x + 161) * (x^7 + 9*x + 161) + sage: list(f._distinct_degree_factorisation_squarefree()) + [(x + 162, 1), (1, 2), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] + + :: + + sage: # needs sage.rings.finite_rings + sage: K. = GF(2^8) + sage: R. = K[] + sage: u1 = x + z8^6 + z8^3 + z8 + sage: u2 = x^2 + (z8^5 + z8^3 + z8^2 + 1)*x + z8^4 + z8^3 + z8 + 1 + sage: u3 = x^3 + z8^7 + z8^4 + z8^3 + z8^2 + z8 + sage: f = u1 * u2 * u3 + sage: list(f._distinct_degree_factorisation_squarefree()) + [(x + z8^6 + z8^3 + z8, 1), + (x^2 + (z8^5 + z8^3 + z8^2 + 1)*x + z8^4 + z8^3 + z8 + 1, 2), + (x^3 + z8^7 + z8^4 + z8^3 + z8^2 + z8, 3)] """ q = self.base_ring().order() # p^k R, x = self.parent().objgen() @@ -2092,6 +2115,42 @@ cdef class Polynomial(CommutativePolynomial): a factor from a polynomial of the form `self = prod g_i(x)` with all `g_i(x)` having the same degree. Uses the Cantor Zassenhaus splitting method. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x^2 + 36*x + 34) * (x^2 + 36*x + 132) * (x^2 + 112*x + 48) + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 112*x + 48 + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 36*x + 34 + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 36*x + 132 + sage: factor = f._cantor_zassenhaus_split_to_irreducible(2) + sage: factor.is_irreducible() + True + sage: f % factor == 0 + True + + :: + + sage: # needs sage.rings.finite_rings + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^2 + z4) * (x + z4^2 + z4 + 1) + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + 1 + sage: factor = f._cantor_zassenhaus_split_to_irreducible(1) + sage: factor.is_irreducible() + True + sage: f % factor == 0 + True """ R = self.parent() q = self.base_ring().order() @@ -2148,6 +2207,42 @@ cdef class Polynomial(CommutativePolynomial): If degree is not None, then only irreducible factors of degree `degree` are searched for, otherwise the smallest degree factor is found. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x^2 + 36*x + 34) * (x^2 + 36*x + 132) * (x^2 + 112*x + 48) + sage: f = (x^2 + 50*x + 28) * (x^5 + 75*x^4 + 87*x^3 + 96*x^2 + 98*x + 40) * (x^7 + 20*x^6 + 16*x^5 + 36*x^4 + 59*x^3 + 3*x^2 + 46*x + 84) + sage: f._any_irreducible_factor_squarefree() + x^2 + 50*x + 28 + sage: f._any_irreducible_factor_squarefree(degree=2) + x^2 + 50*x + 28 + sage: f._any_irreducible_factor_squarefree(degree=5) + x^5 + 75*x^4 + 87*x^3 + 96*x^2 + 98*x + 40 + sage: f._any_irreducible_factor_squarefree(degree=7) + x^7 + 20*x^6 + 16*x^5 + 36*x^4 + 59*x^3 + 3*x^2 + 46*x + 84 + sage: g = (x^2 + 50*x + 28) + sage: g._any_irreducible_factor_squarefree(degree=1) + Traceback (most recent call last): + ... + ValueError: no irreducible factor of degree 1 could be computed from x^2 + 50*x + 28 + + :: + + sage: # needs sage.rings.finite_rings + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^3 + z4^2 + z4) * (x^2 + x + z4^3 + 1) * (x^3 + (z4^3 + z4)*x^2 + z4^2 + 1) + sage: f._any_irreducible_factor_squarefree() + x + z4^3 + z4^2 + z4 + sage: f._any_irreducible_factor_squarefree(degree=1) + x + z4^3 + z4^2 + z4 + sage: f._any_irreducible_factor_squarefree(degree=2) + x^2 + x + z4^3 + 1 + sage: f._any_irreducible_factor_squarefree(degree=3) + x^3 + (z4^3 + z4)*x^2 + z4^2 + 1 """ # If the degree is not None we only want to check a single polynomial if degree is not None: @@ -2173,7 +2268,89 @@ cdef class Polynomial(CommutativePolynomial): def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ - TODO: description with doctests and tests. + Return an irreducible factor of this polynomial. + + INPUT: + + - ``degree`` (None or positive integer) -- Used for polynomials + over finite fields. Attempts to return an irreducible factor of + ``self`` of chosen degree ``degree``. + + - ``assume_squarefree`` (bool) -- Used for polynomials over + finite fields. If ``True``, this polynomial is assumed to be + squarefree. + + - ``assume_distinct_deg`` (bool) -- Used for polynomials over + finite fields. If ``True``, this polynomial is assumed to be + the product of irreducible polynomials of the same degree. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x + 40)^3 * (x^5 + 76*x^4 + 93*x^3 + 112*x^2 + 22*x + 27)^2 * (x^6 + 50*x^5 + 143*x^4 + 162*x^2 + 109*x + 140) + sage: f.any_irreducible_factor() + x + 40 + sage: f.any_irreducible_factor(degree=5) + x^5 + 76*x^4 + 93*x^3 + 112*x^2 + 22*x + 27 + + When the polynomial is known to be squarefree we can optimise the call + by setting ``assume_squarefree`` to be ``True``:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: g = (x - 1) * (x^3 + 7*x + 161) + sage: g.any_irreducible_factor(assume_squarefree=True) + x + 162 + sage: g.any_irreducible_factor(degree=3, assume_squarefree=True) + x^3 + 7*x + 161 + + If we ask for an irreducible factor which does not exist, the function + will throw a ``ValueError``:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: g = (x - 1) * (x^3 + 7*x + 161) + sage: g.any_irreducible_factor(degree=2, assume_squarefree=True) + Traceback (most recent call last): + ... + ValueError: no irreducible factor of degree 2 could be computed from x^4 + 162*x^3 + 7*x^2 + 154*x + 2 + sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + + If we assume that the polynomial is product of irreducible polynomials of the + same degree, we must also supply the degree:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + sage: h.any_irreducible_factor(assume_distinct_deg=True) + Traceback (most recent call last): + ... + ValueError: degree must be known if distinct degree factorisation is assumed + sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random + x + 98 + + Also works for extension fields and even characteristic:: + + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^3 + z4^2)^4 * (x^2 + z4*x + z4) * (x^2 + (z4^3 + z4^2 + z4)*x + z4^2 + z4 + 1) + sage: f.any_irreducible_factor() + x + z4^3 + z4^2 + sage: f.any_irreducible_factor(degree=2) # random + x^2 + (z4^3 + z4^2 + z4)*x + z4^2 + z4 + 1 + + We can also use this function for polynomials which are not defined over finite + fields, but this simply falls back to a slow method of factorisation:: + + sage: R. = ZZ[] + sage: f = 3*x^4 + 2*x^3 + sage: f.any_irreducible_factor() + 3*x + 2 """ # Make sure the user inputted something reasonable for degree if degree is not None: From f0ba87e7978a6120ecffbf9e1728f4d60ba0112b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 22:48:48 +0000 Subject: [PATCH 046/191] every time, whitespace... --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index a1c5745061c..79ca95a2130 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2309,7 +2309,7 @@ cdef class Polynomial(CommutativePolynomial): If we ask for an irreducible factor which does not exist, the function will throw a ``ValueError``:: - + sage: # needs sage.rings.finite_rings sage: F = GF(163) sage: R. = F[] From a9b05b42111b093ffea83cb575271b2298211e9c Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 30 Jan 2024 15:06:06 +0000 Subject: [PATCH 047/191] =?UTF-8?q?apply=20review=20changes=20=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 2 +- src/sage/schemes/elliptic_curves/ell_generic.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 1a3e92aabd8..fdbd3228716 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -613,7 +613,7 @@ def cardinality(self, algorithm=None, extension_degree=1): @cached_method def multiplication_by_p_isogeny(self): r""" - Return the multiplication-by-\(p\) isogeny. + Return the multiplication-by-`p` isogeny. EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 8caa78557ea..411501469b6 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2435,14 +2435,14 @@ def multiplication_by_m(self, m, x_only=False): ....: Qy = g.subs(x=P[0], y=P[1]) ....: assert (P * k).xy() == (Qx, Qy) - However, it still fails for elliptic curves over positive characteristics fields:: + However, it still fails for elliptic curves over positive-characteristic fields:: sage: F. = FunctionField(GF(7)) sage: E = EllipticCurve(F, [a, 1 / a]) sage: E.multiplication_by_m(7) Traceback (most recent call last): ... - NotImplementedError: multiplication by integer not coprime to pis only implemented for curves over finite fields + NotImplementedError: multiplication by integer not coprime to p is only implemented for curves over finite fields :: @@ -2465,7 +2465,7 @@ def multiplication_by_m(self, m, x_only=False): from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): # TODO: Implement the correct formula? - raise NotImplementedError("multiplication by integer not coprime to p" + raise NotImplementedError("multiplication by integer not coprime to p " "is only implemented for curves over finite fields") x, y = polygens(self.base_ring(), 'x,y') From d466ecb2cd3645998f3b1d85f963fb5fcc2dc44c Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 30 Jan 2024 15:07:26 +0000 Subject: [PATCH 048/191] Revert "Remove deprecated `multiplication_by_m_isogeny`" This reverts commit ac0ce58d0f2ea6b7aab6f5cdeb3f3a0ce0fc5821. --- .../schemes/elliptic_curves/ell_generic.py | 95 +++++++++++++++++++ src/sage/schemes/elliptic_curves/hom.py | 15 +++ .../schemes/elliptic_curves/hom_scalar.py | 3 +- 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 411501469b6..6357c7a4a6c 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2514,6 +2514,101 @@ def multiplication_by_m(self, m, x_only=False): return mx, my + def multiplication_by_m_isogeny(self, m): + r""" + Return the ``EllipticCurveIsogeny`` object associated to the + multiplication-by-`m` map on this elliptic curve. + + The resulting isogeny will + have the associated rational maps (i.e., those returned by + :meth:`multiplication_by_m`) already computed. + + NOTE: This function is currently *much* slower than the + result of ``self.multiplication_by_m()``, because + constructing an isogeny precomputes a significant amount + of information. See :trac:`7368` and :trac:`8014` for the + status of improving this situation. + + INPUT: + + - ``m`` -- a nonzero integer + + OUTPUT: + + - An ``EllipticCurveIsogeny`` object associated to the + multiplication-by-`m` map on this elliptic curve. + + EXAMPLES:: + + sage: E = EllipticCurve('11a1') + sage: E.multiplication_by_m_isogeny(7) + doctest:warning ... DeprecationWarning: ... + Isogeny of degree 49 + from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 + over Rational Field + to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 + over Rational Field + + TESTS: + + Tests for :trac:`32490`:: + + sage: E = EllipticCurve(QQbar, [1,0]) # needs sage.rings.number_field + sage: E.multiplication_by_m_isogeny(1).rational_maps() # needs sage.rings.number_field + (x, y) + + :: + + sage: E = EllipticCurve_from_j(GF(31337).random_element()) # needs sage.rings.finite_rings + sage: P = E.random_point() # needs sage.rings.finite_rings + sage: [E.multiplication_by_m_isogeny(m)(P) == m*P for m in (1,2,3,5,7,9)] # needs sage.rings.finite_rings + [True, True, True, True, True, True] + + :: + + sage: E = EllipticCurve('99.a1') + sage: E.multiplication_by_m_isogeny(5) + Isogeny of degree 25 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field + sage: E.multiplication_by_m_isogeny(2).rational_maps() + ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), + (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) + + Test for :trac:`34727`:: + + sage: E = EllipticCurve([5,5]) + sage: E.multiplication_by_m_isogeny(-1) + Isogeny of degree 1 + from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-2) + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-3) + Isogeny of degree 9 + from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: mu = E.multiplication_by_m_isogeny + sage: all(mu(-m) == -mu(m) for m in (1,2,3,5,7)) + True + """ + from sage.misc.superseded import deprecation + deprecation(32826, 'The .multiplication_by_m_isogeny() method is superseded by .scalar_multiplication().') + + mx, my = self.multiplication_by_m(m) + + torsion_poly = self.torsion_polynomial(abs(m)).monic() + phi = self.isogeny(torsion_poly, codomain=self) + phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) + + # trac 32490: using codomain=self can give a wrong isomorphism + for aut in self.automorphisms(): + psi = aut * phi + if psi.rational_maps() == (mx, my): + return psi + + assert False, 'bug in multiplication_by_m_isogeny()' + def scalar_multiplication(self, m): r""" Return the scalar-multiplication map `[m]` on this elliptic diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index a7688d3099b..db3b1918926 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -230,6 +230,21 @@ def _richcmp_(self, other, op): sage: phi.dual() == psi.dual() True + :: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism + sage: E = EllipticCurve([9,9]) + sage: F = E.change_ring(GF(71)) + sage: wE = identity_morphism(E) + sage: wF = identity_morphism(F) + sage: mE = E.scalar_multiplication(1) + sage: mF = F.multiplication_by_m_isogeny(1) + doctest:warning ... DeprecationWarning: ... + sage: [mE == wE, mF == wF] + [True, True] + sage: [a == b for a in (wE,mE) for b in (wF,mF)] + [False, False, False, False] + .. SEEALSO:: - :meth:`_comparison_impl` diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index b09a538ed8c..711c1c66edf 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -381,7 +381,8 @@ def scaling_factor(self): sage: u = phi.scaling_factor() sage: u == phi.formal()[1] True - sage: u == 5 + sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() + doctest:warning ... DeprecationWarning: ... True The scaling factor lives in the base ring:: From 517d13ea844ea16e98cf96d33455c09feeb91247 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Tue, 30 Jan 2024 16:50:45 +0100 Subject: [PATCH 049/191] gather several calls to inversion (computing identical inverses) before the loop --- src/sage/matrix/matrix2.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 2a069660011..b3543f4d9a2 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -13611,8 +13611,9 @@ cdef class Matrix(Matrix1): if max_location != -1: perm[k], perm[max_location] = perm[max_location], perm[k] M.swap_rows(k, max_location) + inv = M.get_unsafe(k, k).inverse() for j in range(k+1, m): - scale = -M.get_unsafe(j, k)/M.get_unsafe(k, k) + scale = -M.get_unsafe(j, k)*inv M.set_unsafe(j, k, -scale) for p in range(k+1, n): M.set_unsafe(j, p, M.get_unsafe(j, p) + scale*M.get_unsafe(k, p)) From d7b0aeb66e477cba4a30cee67f3ceaa3e6a46dbc Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 30 Jan 2024 19:06:05 +0100 Subject: [PATCH 050/191] convert variables to be substituted to generators of the parent of numerator and denominator, unify subs signature, allow generator indices as keys consistently --- src/sage/rings/fraction_field_FpT.pyx | 4 +-- src/sage/rings/fraction_field_element.pyx | 25 ++++++++++++----- .../polynomial/multi_polynomial_element.py | 22 ++++++++------- .../rings/polynomial/polynomial_element.pyx | 27 ++++++++++--------- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 4c99862ca24..f2c35b8eb0a 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -268,7 +268,7 @@ cdef class FpTElement(FieldElement): """ return self.numer()(*args, **kwds) / self.denom()(*args, **kwds) - def subs(self, *args, **kwds): + def subs(self, in_dict=None, **kwds): """ EXAMPLES:: @@ -280,7 +280,7 @@ cdef class FpTElement(FieldElement): sage: f.subs(X=2) (t + 1)/(t + 10) """ - return self.numer().subs(*args, **kwds) / self.denom().subs(*args, **kwds) + return self.numer().subs(in_dict, **kwds) / self.denom().subs(in_dict, **kwds) def valuation(self, v): """ diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 5f3d122b1bd..15e6087b1fa 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -446,12 +446,14 @@ cdef class FractionFieldElement(FieldElement): """ return self._numerator(*x, **kwds) / self._denominator(*x, **kwds) - def subs(self, *args, **kwds): - """ + def subs(self, in_dict=None, **kwds): + r""" Substitute variables in the numerator and denominator of ``self``. - All the arguments are transmitted unchanged to the method - ``subs`` of the numerator and the denominator. + If a dictionary is passed, the keys are mapped to generators + of the parent ring. Otherwise, the arguments are transmitted + unchanged to the method ``subs`` of the numerator and the + denominator. EXAMPLES:: @@ -472,8 +474,19 @@ cdef class FractionFieldElement(FieldElement): (x1 + 2*x2 + 3*x3 + 400)/(x1 + 2*x2 + 3*x3 + 5*x5 + 6*x6 + 7*x7 + 400) """ - num = self._numerator.subs(*args, **kwds) - den = self._denominator.subs(*args, **kwds) + if in_dict: + gens = self.parent().gens() + + def to_R(m): + try: + mi = gens.index(m) + except ValueError: + return m + return mi + in_dict = {to_R(m): v for m, v in in_dict.items()} + + num = self._numerator.subs(in_dict, **kwds) + den = self._denominator.subs(in_dict, **kwds) return num / den substitute = subs diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index f5c1b0e480c..743a7b6842f 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -178,8 +178,8 @@ def __call__(self, *x, **kwds): except AttributeError: K = self.parent().base_ring() y = K(0) - for (m,c) in self.element().dict().items(): - y += c*prod([ x[i]**m[i] for i in range(n) if m[i] != 0]) + for m, c in self.element().dict().items(): + y += c * prod(v ** e for v, e in zip(x, m) if e) return y def _richcmp_(self, right, op): @@ -1422,7 +1422,7 @@ def is_term(self): """ return len(self.element()) == 1 - def subs(self, fixed=None, **kw): + def subs(self, fixed=None, **kwds): """ Fix some given variables in a given multivariate polynomial and return the changed multivariate polynomials. The polynomial itself @@ -1434,10 +1434,9 @@ def subs(self, fixed=None, **kw): INPUT: - - ``fixed`` - (optional) dictionary of inputs - - ``**kw`` - named parameters + - ``**kwds`` - named parameters OUTPUT: new :class:`MPolynomial` @@ -1453,11 +1452,14 @@ def subs(self, fixed=None, **kw): 25*y^2 + y + 30 """ variables = list(self.parent().gens()) - for i in range(0,len(variables)): - if str(variables[i]) in kw: - variables[i] = kw[str(variables[i])] - elif fixed and variables[i] in fixed: - variables[i] = fixed[variables[i]] + for i in range(len(variables)): + if str(variables[i]) in kwds: + variables[i] = kwds[str(variables[i])] + elif fixed: + if variables[i] in fixed: + variables[i] = fixed[variables[i]] + elif i in fixed: + variables[i] = fixed[i] return self(tuple(variables)) def monomials(self): diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4c9cf2f7e0d..dde8a42123a 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -410,11 +410,9 @@ cdef class Polynomial(CommutativePolynomial): return self._parent.zero() return self * self._parent(right) - def subs(self, *x, **kwds): + def subs(self, in_dict=None, **kwds): r""" - Identical to ``self(*x)``. - - See the docstring for :meth:`__call__`. + Substitute the variable in ``self``. EXAMPLES:: @@ -433,14 +431,19 @@ cdef class Polynomial(CommutativePolynomial): ... TypeError: keys do not match self's parent """ - if len(x) == 1 and isinstance(x[0], dict): - g = self._parent.gen() - if g in x[0]: - return self(x[0][g]) - elif len(x[0]) > 0: - raise TypeError("keys do not match self's parent") - return self - return self(*x, **kwds) + if not in_dict: + return self(**kwds) + + if isinstance(in_dict, dict): + if len(in_dict) > 1: + raise TypeError("only the generator can be substituted, use __call__ instead") + k, v = next(iter(in_dict.items())) + if not k or k == self._parent.gen(): + return self(v, **kwds) + raise TypeError("keys do not match self's parent") + + return self(in_dict, **kwds) + substitute = subs @cython.boundscheck(False) From a5ab1b485ba43ac00485bf2c2377cab22687b15f Mon Sep 17 00:00:00 2001 From: lucasgagnon <122184619+lucasgagnon@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:41:14 -0500 Subject: [PATCH 051/191] Update ncsym.py Fix to ncsym.ncsym.nesting function. --- src/sage/combinat/ncsym/ncsym.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 3f1476fb15c..6cff509724d 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -103,6 +103,13 @@ def nesting(la, nu): sage: nesting(set(mu).difference(nu), nu) 1 + sage: A = SetPartition([[1], [2,5], [3,4]]) + sage: B = SetPartition([[1,3,4], [2,5]]) + sage: nesting(A, B) + 1 + sage: nesting(B, A) + 1 + :: sage: lst = list(SetPartitions(4)) @@ -135,11 +142,13 @@ def nesting(la, nu): nst = 0 for p in la: p = sorted(p) - for i in range(len(p)-1): - for a in arcs: - if a[0] >= p[i]: + for a in arcs: + if p[-1] < a[0]: + continue + for i in range(len(p)-1): + if a[1] <= p[i+1]: break - if p[i+1] < a[1]: + if a[0] < p[i]: nst += 1 return nst From bbb27866104675060ce91dc52227316209c5223e Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 30 Jan 2024 18:44:08 +0000 Subject: [PATCH 052/191] fix mod p thing --- src/sage/schemes/elliptic_curves/ell_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 6357c7a4a6c..cd6ece02e2c 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2463,7 +2463,7 @@ def multiplication_by_m(self, m, x_only=False): x = polygen(self.base_ring(), 'x') else: from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic - if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): + if p != 0 and m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): # TODO: Implement the correct formula? raise NotImplementedError("multiplication by integer not coprime to p " "is only implemented for curves over finite fields") From 43a175fd1ce24544b6fc96e6fd165b768bdc64fb Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Tue, 30 Jan 2024 21:02:40 +0100 Subject: [PATCH 053/191] fix as suggested --- src/sage/matrix/matrix2.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index b3543f4d9a2..34b5808d0c8 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -13613,7 +13613,7 @@ cdef class Matrix(Matrix1): M.swap_rows(k, max_location) inv = M.get_unsafe(k, k).inverse() for j in range(k+1, m): - scale = -M.get_unsafe(j, k)*inv + scale = -M.get_unsafe(j, k) * inv M.set_unsafe(j, k, -scale) for p in range(k+1, n): M.set_unsafe(j, p, M.get_unsafe(j, p) + scale*M.get_unsafe(k, p)) From 62ff2122a8e1f0d758870eb8f33fa50b9257dc90 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 30 Jan 2024 22:23:31 +0100 Subject: [PATCH 054/191] make FreeAlgebra graded and provide is_unit and __invert__ --- src/sage/algebras/free_algebra.py | 23 ++++++++++-- src/sage/algebras/free_algebra_element.py | 44 +++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index f8c91675c97..427fa8175d0 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -163,7 +163,7 @@ from sage.misc.lazy_import import lazy_import from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.ring import Algebra -from sage.categories.algebras_with_basis import AlgebrasWithBasis +from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.words.word import Word from sage.structure.category_object import normalize_names @@ -467,7 +467,7 @@ def __init__(self, R, n, names): raise TypeError("argument R must be a ring") self.__ngens = n indices = FreeMonoid(n, names=names) - cat = AlgebrasWithBasis(R) + cat = GradedAlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, indices, prefix='F', category=cat) self._assign_names(indices.variable_names()) @@ -774,6 +774,23 @@ def gens(self): """ return tuple(self.gen(i) for i in range(self.__ngens)) + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: A. = FreeAlgebra(QQ) + sage: m = A.basis().keys()[10] + sage: m + a*b^2 + sage: A.degree_on_basis(m) + 3 + sage: (a*b^2).degree() + 3 + """ + return len(m) + def product_on_basis(self, x, y): """ Return the product of the basis elements indexed by ``x`` and ``y``. @@ -1149,7 +1166,7 @@ def __init__(self, alg): """ R = alg.base_ring() self._alg = alg - category = AlgebrasWithBasis(R) + category = GradedAlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, alg.monoid(), prefix='PBW', category=category) self._assign_names(alg.variable_names()) diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index 1854414e2d0..e30e5f7d917 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -203,6 +203,50 @@ def _mul_(self, y): del z_elt[key] return A._from_dict(z_elt) + def is_unit(self): + r""" + Return ``True`` if ``self`` is invertible. + + EXAMPLES:: + + sage: A. = FreeAlgebra(ZZ) + sage: A(-1).is_unit() + True + sage: A(2).is_unit() + False + sage: A(1 + x).is_unit() + False + """ + if self.is_zero() or not self.is_homogeneous() or self.degree(): + return False + c = self.leading_coefficient() + return c.is_unit() + + def __invert__(self): + """ + EXAMPLES:: + + sage: A. = FreeAlgebra(QQ) + sage: ~A(1) + 1 + + TESTS:: + + sage: ~A(0) + Traceback (most recent call last): + ... + ZeroDivisionError: element is not invertible + + sage: ~A(1 + x) + Traceback (most recent call last): + ... + ZeroDivisionError: element is not invertible + """ + if self.is_unit(): + c = self.leading_coefficient() + return self.parent()(~c) + raise ZeroDivisionError("element is not invertible") + def _acted_upon_(self, scalar, self_on_left=False): """ Return the action of a scalar on ``self``. From c65548f72226343350b5378c9d8e24ccc7c7d98e Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Tue, 30 Jan 2024 23:43:34 +0000 Subject: [PATCH 055/191] Ensure that the roots returned are of the expected type and add a note about this --- src/sage/rings/polynomial/polynomial_element.pyx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 79ca95a2130..e4e6f820b77 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2601,10 +2601,17 @@ cdef class Polynomial(CommutativePolynomial): raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") return f.any_root() - # Otherwise change the ring of this degree `degree` irreducible - # polynomial and attempt to find a root from this - F_ext = self.base_ring().extension(f, names="a") - return F_ext.gen() + # Otherwise compute an extension ring of the correct degree and + # compute a root + # TODO: a faster option would be to create an extension with `f` + # as F_ext = self.base_ring().extension(f, names="a") + # however this returns a quotient ring rather than a + # FiniteField type if the base field is a non-prime field, + # and this slower option is chosen to ensure the root is + # over explicitly a FiniteField type. + F_ext = self.base_ring().extension(f.degree(), names="a") + f = f.change_ring(F_ext) + return f.any_root() def __truediv__(left, right): r""" From e4888173cf3501b8ba9bef42a4a2bd99a77abb33 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Tue, 30 Jan 2024 23:50:52 +0000 Subject: [PATCH 056/191] Whitespace --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e4e6f820b77..d4ebbf5702d 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2603,11 +2603,11 @@ cdef class Polynomial(CommutativePolynomial): # Otherwise compute an extension ring of the correct degree and # compute a root - # TODO: a faster option would be to create an extension with `f` + # TODO: a faster option would be to create an extension with `f` # as F_ext = self.base_ring().extension(f, names="a") # however this returns a quotient ring rather than a # FiniteField type if the base field is a non-prime field, - # and this slower option is chosen to ensure the root is + # and this slower option is chosen to ensure the root is # over explicitly a FiniteField type. F_ext = self.base_ring().extension(f.degree(), names="a") f = f.change_ring(F_ext) From b207c56c1c0aa04ec0e1e7286d307b6057e82c23 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jan 2024 18:49:15 +0900 Subject: [PATCH 057/191] Initial version of AR quivers. --- src/doc/en/reference/quivers/index.rst | 1 + src/sage/graphs/digraph.py | 18 + src/sage/quivers/ar_quiver.py | 915 +++++++++++++++++++++++++ 3 files changed, 934 insertions(+) create mode 100644 src/sage/quivers/ar_quiver.py diff --git a/src/doc/en/reference/quivers/index.rst b/src/doc/en/reference/quivers/index.rst index 98bf561c4dc..3650919b338 100644 --- a/src/doc/en/reference/quivers/index.rst +++ b/src/doc/en/reference/quivers/index.rst @@ -6,6 +6,7 @@ Quivers sage/quivers/algebra sage/quivers/algebra_elements + sage/quivers/ar_quiver sage/quivers/homspace sage/quivers/morphism sage/quivers/path_semigroup diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 0c6c309721f..929fa9f31a7 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -70,6 +70,7 @@ :delim: | :meth:`~DiGraph.path_semigroup` | Return the (partial) semigroup formed by the paths of the digraph. + :meth:`~DiGraph.auslander_reiten_quiver` | Return the Auslander-Reiten quiver of ``self``. **Connectivity:** @@ -3180,6 +3181,23 @@ def path_semigroup(self): from sage.quivers.path_semigroup import PathSemigroup return PathSemigroup(self) + def auslander_reiten_quiver(self): + r""" + Return the Auslander-Reiten quiver of ``self``. + + .. SEEALSO:: + + :class:`~sage.quivers.ar_quiver.AuslanderReitenQuiver` + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: D.auslander_reiten_quiver() + Auslander-Reiten quiver of Multi-digraph on 2 vertices + """ + from sage.quivers.ar_quiver import AuslanderReitenQuiver + return AuslanderReitenQuiver(self) + # Directed Acyclic Graphs (DAGs) def topological_sort(self, implementation="default"): diff --git a/src/sage/quivers/ar_quiver.py b/src/sage/quivers/ar_quiver.py new file mode 100644 index 00000000000..daa1e755fa5 --- /dev/null +++ b/src/sage/quivers/ar_quiver.py @@ -0,0 +1,915 @@ +r""" +Auslander-Reiten Quivers +""" + +# **************************************************************************** +# Copyright (C) 2024 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.structure.element import Element +from sage.structure.richcmp import richcmp +from sage.structure.global_options import GlobalOptions +from sage.categories.sets_cat import Sets +from sage.sets.family import Family +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.free_module import CombinatorialFreeModule +from sage.rings.integer_ring import ZZ +from sage.misc.cachefunc import cached_method +from sage.graphs.digraph import DiGraph + + +class AuslanderReitenQuiver(UniqueRepresentation, Parent): + r""" + The Auslander-Reiten quiver. + + Let `Q = (Q_0, Q_1)` be a finite acyclic quiver. The + *Auslander-Reiten quiver* (AR quiver) `\Gamma_Q` is the quiver + whose vertices correspond to the indecompositible modules of `Q` + (equivalently its path algebra over an algebraically closed field) + and edges are irreducible morphisms. + + In this implementation, we denote the vertices of `\Gamma_Q` as + certain pairs `\langle v, k \rangle`, where `v \in Q_0` and + `k \in \ZZ \setminus \{0\}` is called the *level*. When `k > 0` + (resp. `k < 0`), then it corresponds to a preprojective (resp. + postinjective) module. When the quiver is a finite type Dynkin + quiver, we consider all modules to be preprojectives and denoted + by a positive level. + + .. NOTE:: + + We use the terminology *postinjective* instead of *preinjective* + given that they follow from injectives by AR translation. + + ALGORITHM: + + We compute the dimension vectors of a projective `\langle v, 1 \rangle` + by counting the number of (directed) paths `u \to v` in `Q`. We then + proceed inductively to compute all of the dimension vectors of level + `k` by using the translation equation + + .. MATH:: + + dim \langle v, k-1 \rangle + \dim \langle v, k \rangle + = \sum_{u,k'} \dim \langle u, k' \rangle, + + where the sum is over all paths from `\langle v, k-1 \rangle` to + `\langle v, k \rangle` in `\Gamma_Q`. More specifically, for each edge + `(u, v, \ell) \in Q_1` (resp. `(v, u, \ell) \in Q_1`), we have + `\langle u, k-1 \rangle` (resp. `\langle u, k \rangle`) in the sum + (assuming the node is in the AR quiver). + + The algorithm for postinjectives is dual to the above. + + .. TODO:: + + This only is implemented for the preprojectives and postinjectives + when the quiver is not a finite type Dynkin quiver. + + .. TODO:: + + Implement this for general Artinian algebras. + + EXAMPLES: + + We create the AR quivers for finite type `A_3` Dynkin quivers:: + + sage: DA = DiGraph([[1, 2], [2, 3]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 2>), (<2, 1>, <1, 1>), (<2, 1>, <3, 2>), (<3, 1>, <2, 1>), + (<2, 2>, <3, 3>), (<3, 2>, <2, 2>)] + + sage: DA = DiGraph([[1, 2], [3, 2]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 2>), (<2, 1>, <1, 1>), (<2, 1>, <3, 1>), (<3, 1>, <2, 2>), + (<2, 2>, <1, 2>), (<2, 2>, <3, 2>)] + + sage: DA = DiGraph([[2, 1], [2, 3]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 1>), (<2, 1>, <1, 2>), (<2, 1>, <3, 2>), (<3, 1>, <2, 1>), + (<1, 2>, <2, 2>), (<3, 2>, <2, 2>)] + + sage: DA = DiGraph([[2, 1], [3, 2]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.digraph().edges(labels=False) + [(<1, 1>, <2, 1>), (<2, 1>, <3, 1>), (<2, 1>, <1, 2>), (<3, 1>, <2, 2>), + (<1, 2>, <2, 2>), (<2, 2>, <1, 3>)] + + An example for the type `D_5` Dynkin quiver:: + + sage: DD = DiGraph([[5,3], [4,3], [3,2], [2,1]]) + sage: AR = DD.auslander_reiten_quiver() + sage: AR + Auslander-Reiten quiver of a ['D', 5] Dynkin quiver + sage: len(list(DD)) + 5 + + An `E_8` Dynkin quiver:: + + sage: DE = DiGraph([[8,7], [7,6], [5,6], [5,3], [3,4], [3,2], [2,1]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR + Auslander-Reiten quiver of a ['E', 8] Dynkin quiver + sage: len(list(AR)) + 120 + sage: len(list(RootSystem(['E', 8]).root_lattice().positive_roots())) + 120 + + The Kronecker quiver:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: for i in range(1, 5): + ....: for v in D.vertices(): + ....: pp = AR(v, i) + ....: pi = AR(v, -i) + ....: print(pp, pp.dimension_vector(), " ", pi, pi.dimension_vector()) + <1, 1> v1 + 2*v2 <1, -1> v1 + <2, 1> v2 <2, -1> 2*v1 + v2 + <1, 2> 3*v1 + 4*v2 <1, -2> 3*v1 + 2*v2 + <2, 2> 2*v1 + 3*v2 <2, -2> 4*v1 + 3*v2 + <1, 3> 5*v1 + 6*v2 <1, -3> 5*v1 + 4*v2 + <2, 3> 4*v1 + 5*v2 <2, -3> 6*v1 + 5*v2 + <1, 4> 7*v1 + 8*v2 <1, -4> 7*v1 + 6*v2 + <2, 4> 6*v1 + 7*v2 <2, -4> 8*v1 + 7*v2 + """ + @staticmethod + def __classcall_private__(cls, quiver): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: D = DiGraph([[1,2], [2,3], [3,1]]) + sage: D.auslander_reiten_quiver() + Traceback (most recent call last): + ... + ValueError: the quiver must not have cycles + """ + if quiver.has_loops() or not quiver.is_directed_acyclic(): + raise ValueError("the quiver must not have cycles") + quiver = quiver.copy(immutable=True) + return super().__classcall__(cls, quiver) + + def __init__(self, quiver): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,6], [6,5], [5,3], [3,4], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: TestSuite(AR).run() + + sage: D = DiGraph([[1,2], [3,4]]) + sage: AR = D.auslander_reiten_quiver() + sage: TestSuite(AR).run() + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: TestSuite(AR).run() + """ + self._quiver = quiver + self._top_sort = quiver.topological_sort() + self._dim_vec_space = CombinatorialFreeModule(ZZ, quiver.vertices(), prefix='v', bracket=False) + self._max_level = float('inf') + + dynkin_type = detect_dynkin_quiver(quiver) + if dynkin_type is not None: + self._cartan_type = dynkin_type + self._is_finite = dynkin_type is not None + cat = Sets().Enumerated().Finite() if self._is_finite else Sets().Infinite() + super().__init__(self, category=cat) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[5,6], [5,3], [3,4], [2,3], [1,2]]) + sage: DE.auslander_reiten_quiver() + Auslander-Reiten quiver of a ['E', 6] Dynkin quiver + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: D.auslander_reiten_quiver() + Auslander-Reiten quiver of Multi-digraph on 2 vertices + """ + if self._is_finite: + return "Auslander-Reiten quiver of a {} Dynkin quiver".format(self._cartan_type) + return "Auslander-Reiten quiver of {}".format(self._quiver) + + # add options to class + class options(GlobalOptions): + r""" + Sets and displays the global options for Auslander-Reiten quivers. + If no parameters are set, then the function returns a copy of the + options dictionary. + + The ``options`` to partitions can be accessed as the method + :obj:`AuslanderReitenQuiver.options` of + :class:`~sage.quivers.ar_quiver.AuslanderReitenQuiver`. + + @OPTIONS@ + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: node = AR(2, 2) + sage: latex(node) + \left\langle 2, 2 \right\rangle + sage: AR.options.latex = "dimension_vector" + sage: latex(node) + 2 v_{1} + 3 v_{2} + sage: AR.options.latex = "both" + sage: latex(node) + \begin{gathered} \left\langle 2, 2 \right\rangle \\ 2 v_{1} + 3 v_{2} \end{gathered} + sage: AR.options._reset() + """ + NAME = 'AuslanderReitenQuiver' + module = 'sage.quivers.ar_quiver' + latex = dict(default="node", + description='Specifies how nodes of the AR quiver should be latexed', + values=dict(node='latex as the node description', + dimension_vector='latex as the dimension vector', + both='latex as both'), + case_sensitive=False) + + def _an_element_(self): + r""" + Return an element of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,8], [7,6], [5,6], [3,5], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR._an_element_() + <1, 1> + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR._an_element_() + <1, 1> + """ + return next(iter(self.projectives())) + + def quiver(self): + r""" + Return the quiver defining ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,8], [7,6], [5,6], [3,5], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR.quiver() == DE + True + """ + return self._quiver + + def projectives(self): + r""" + Return the projectives of ``self``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR.projectives() + Finite family {1: <1, 1>, 2: <2, 1>} + """ + return Family({v: self.element_class(self, v, 1) for v in self._quiver.vertex_iterator()}) + + @cached_method + def simples(self): + r""" + Return the simples of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,8], [7,6], [5,6], [3,5], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR.simples() + Finite family {1: <1, 15>, 2: <1, 14>, 3: <8, 4>, 4: <4, 15>, + 5: <8, 3>, 6: <6, 1>, 7: <7, 15>, 8: <8, 1>} + """ + ret = {} + for elt in self: + supp = elt.dimension_vector().support() + if len(supp) != 1: + continue + ret[next(iter(supp))] = elt + return Family(ret) + + def injectives(self): + r""" + Return the injectives of ``self``. + + EXAMPLES:: + + sage: DE = DiGraph([[7,6], [6,5], [5,3], [4,3], [2,3], [1,2]]) + sage: AR = DE.auslander_reiten_quiver() + sage: AR.injectives() + Finite family {1: <1, 9>, 2: <2, 9>, 3: <3, 9>, 4: <4, 9>, + 5: <5, 9>, 6: <6, 9>, 7: <7, 9>} + """ + if self._is_finite: + self.digraph() # sets self._injective attribute + return self._injectives + return Family({v: self(v, -1) for v in self._quiver.vertex_iterator()}) + + def _digraph_set_latex_options(self, G): + """ + Set the latex options of the digraph ``G``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: G = AR.digraph_preprojectives(2) + sage: G = AR._digraph_set_latex_options(G) + sage: G.latex_options().get_option('edge_labels') + True + """ + G.set_latex_options(edge_labels=True) + + from sage.graphs.dot2tex_utils import have_dot2tex + if have_dot2tex(): + from sage.misc.latex import LatexExpr + + def edge_options(data): + u, v, l = data + edge_opts = {} + if l == 'ART': + edge_opts["color"] = "dashed,blue" + edge_opts["label"] = LatexExpr(r"\tau") + return edge_opts + + G.set_latex_options(format="dot2tex", edge_options=edge_options) + return G + + def digraph_preprojectives(self, max_depth, with_translations=False): + r""" + Return the diagraph of preprojectives of ``self`` up to ``max_depth``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: G = AR.digraph_preprojectives(3) + sage: [node.dimension_vector() for node in G] + [v1 + 2*v2, v2, 3*v1 + 4*v2, 2*v1 + 3*v2, 5*v1 + 6*v2, 4*v1 + 5*v2] + sage: AR.digraph_preprojectives(0) + Digraph on 0 vertices + """ + if max_depth < 1: + return self._digraph_set_latex_options(DiGraph()) + + k = 2 + prev = dict(self.projectives()) + verts = list(prev.values()) + edges = [(prev[v], prev[u], l) for u, v, l in self._quiver.edge_iterator()] + cur = self._dim_vecs_level(k) + while k <= max_depth: + # convert cur to the appropriate data + cur = {v: self.element_class(self, v, k) for v in cur} + verts.extend(cur.values()) + edges.extend((cur[v], cur[u], l) + for u in cur for _, v, l in self._quiver.outgoing_edge_iterator(u) if v in cur) + edges.extend((prev[u], cur[v], l) for v in cur + for u, _, l in self._quiver.incoming_edge_iterator(v) if u in prev) + if with_translations: + edges.extend((cur[v], prev[v], 'ART') for v in cur if v in prev) + k += 1 + prev = cur + cur = self._dim_vecs_level(k) + + G = DiGraph([verts, edges], format="vertices_and_edges", multiedges=True, immutable=True) + return self._digraph_set_latex_options(G) + + def digraph_postinjectives(self, max_depth, with_translations=False): + """ + Return the diagraph of postinjectives of ``self`` up to ``max_depth``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: G = AR.digraph_postinjectives(3) + sage: [node.dimension_vector() for node in G] + [5*v1 + 4*v2, 6*v1 + 5*v2, 3*v1 + 2*v2, 4*v1 + 3*v2, v1, 2*v1 + v2] + sage: AR.digraph_postinjectives(0) + Digraph on 0 vertices + """ + if max_depth < 1: + return self._digraph_set_latex_options(DiGraph()) + + k = 2 + prev = dict(self.injectives()) + verts = list(prev.values()) + edges = [(prev[u], prev[v], l) for u, v, l in self._quiver.edge_iterator()] + cur = self._dim_vecs_level(-k) + while k <= max_depth: + # convert cur to the appropriate data + cur = {v: self.element_class(self, v, -k) for v in cur} + verts.extend(cur.values()) + edges.extend((cur[u], cur[v], l) + for u in cur for _, v, l in self._quiver.outgoing_edge_iterator(u) if v in cur) + edges.extend((cur[v], prev[u], l) for v in cur + for u, _, l in self._quiver.incoming_edge_iterator(v) if u in prev) + if with_translations: + edges.extend((prev[v], cur[v], 'ART') for v in cur if v in prev) + k += 1 + prev = cur + cur = self._dim_vecs_level(-k) + + G = DiGraph([verts, edges], format="vertices_and_edges", multiedges=True, immutable=True) + return self._digraph_set_latex_options(G) + + @cached_method + def digraph(self, with_translations=False): + r""" + Return the diagraph of ``self``. + + INPUT: + + - ``with_translations`` -- (default: ``False``) if ``True``, then + include the arrows corresponding to the translations. + + EXAMPLES:: + + sage: DA = DiGraph([[1,2]]) + sage: AR = DA.auslander_reiten_quiver() + sage: G = AR.digraph(); G + Digraph on 3 vertices + sage: G.edges() + [(<1, 1>, <2, 2>, None), (<2, 1>, <1, 1>, None)] + sage: GT = AR.digraph(with_translations=True) + sage: GT.edges() + [(<1, 1>, <2, 2>, None), (<2, 1>, <1, 1>, None), (<2, 2>, <2, 1>, 'ART')] + """ + if not self._is_finite: + raise TypeError("the AR quiver is not finite") + + if with_translations: + G = self.digraph().copy(immutable=False) + for v in G.vertex_iterator(): + u = v.translation() + if u is not None: + G.add_edge(v, u, 'ART') + G = G.copy(immutable=True) + + else: + k = 2 + prev = dict(self.projectives()) + injectives = dict(prev) # make a shallow copy since we will mutate it + verts = list(prev.values()) + edges = [(prev[v], prev[u], l) for u, v, l in self._quiver.edge_iterator()] + cur = self._dim_vecs_level(k) + while cur: + # convert cur to the appropriate data + cur = {v: self.element_class(self, v, k) for v in cur} + injectives.update(cur) + verts.extend(cur.values()) + edges.extend((cur[v], cur[u], l) + for u in cur for _, v, l in self._quiver.outgoing_edge_iterator(u) if v in cur) + edges.extend((prev[u], cur[v], l) for v in cur + for u, _, l in self._quiver.incoming_edge_iterator(v) if u in prev) + k += 1 + prev = cur + cur = self._dim_vecs_level(k) + + self._injectives = Family(injectives) + G = DiGraph([verts, edges], format="vertices_and_edges", immutable=True) + + return self._digraph_set_latex_options(G) + + def __iter__(self): + r""" + Iterate over ``self`` when possible. + + EXAMPLES:: + + sage: DD = DiGraph([[3,2], [4,2], [2,1]]) + sage: AR = DD.auslander_reiten_quiver() + sage: list(AR) + [<1, 1>, <2, 1>, <3, 1>, <4, 1>, <1, 2>, <2, 2>, <3, 2>, <4, 2>, + <1, 3>, <2, 3>, <3, 3>, <4, 3>] + """ + return iter(self.digraph()) + + def _element_constructor_(self, vertex, level=None): + r""" + Construct an element of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR(2, 2) + <2, 2> + sage: AR((2, 3)) + <2, 3> + sage: AR(2, 4) + Traceback (most recent call last): + ... + ValueError: no 2 at level 4 + sage: AR(2, 1/2) + Traceback (most recent call last): + ... + ValueError: the level 1/2 must be an integer + sage: AR(10, 1) + Traceback (most recent call last): + ... + ValueError: 10 is not a vertex of the quiver + """ + if level is None: + if len(vertex) == 2: + vertex, level = vertex + if vertex not in self._quiver: + raise ValueError(f"{vertex} is not a vertex of the quiver") + if level == 1: + return self.element_class(self, vertex, level) + if level not in ZZ: + raise ValueError(f"the level {level} must be an integer") + + if not self._is_finite: + if level == -1 or vertex in self._dim_vecs_level(level): + return self.element_class(self, vertex, level) + # This is likely never true + raise ValueError(f"no {vertex} at level {level}") + + # otherwise the AR quiver is finite + if level < 0: + self.digraph() # computes the max level + level = self._max_level - level + if level > 1: + if vertex in self._dim_vecs_level(level): + return self.element_class(self, vertex, level) + raise ValueError(f"no {vertex} at level {level}") + + @cached_method + def _dim_vecs_level(self, k): + r""" + Return a ``dict`` of dimension vectors of level ``k``. + + .. WARNING:: + + This is only meant to be used internally as the output is + mutable but cached. Thus, the output should not be changed. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR._dim_vecs_level(1) + {1: v1, 2: v1 + v2, 3: v1 + v2 + v3, 4: v1 + v2 + v3 + v4} + sage: AR._dim_vecs_level(2) + {1: v2, 2: v2 + v3, 3: v2 + v3 + v4} + sage: AR._dim_vecs_level(3) + {1: v3, 2: v3 + v4} + sage: AR._dim_vecs_level(4) + {1: v4} + sage: AR._dim_vecs_level(-1) + {1: v1 + v2 + v3 + v4, 2: v2 + v3 + v4, 3: v3 + v4, 4: v4} + sage: AR._dim_vecs_level(-2) + {2: v1 + v2 + v3, 3: v2 + v3, 4: v3} + sage: AR._dim_vecs_level(-3) + {3: v1 + v2, 4: v2} + sage: AR._dim_vecs_level(-4) + {4: v1} + """ + if k == 0: + raise ValueError("k must not be 0") + M = self._dim_vec_space + Q = self._quiver + if k == 1: + ret = {v: M._from_dict({u: ZZ(len(Q.all_paths(v, u, use_multiedges=True))) for u in Q.vertex_iterator()}) + for v in Q.vertex_iterator()} + elif k > 1: + if k > self._max_level: + return {} + prev = self._dim_vecs_level(k-1) + if k > self._max_level: # this might get set on the recursive call + return {} + ret = {} + for v in reversed(self._top_sort): + if v not in prev: # assumption: this vertex will never reappear + continue + temp = -prev[v] + for u, _, _ in Q.incoming_edge_iterator(v): + if u in prev: + temp += prev[u] + for _, u, _ in Q.outgoing_edge_iterator(v): + if u in ret: + temp += ret[u] + if all(coeff > 0 for key, coeff in temp): + ret[v] = temp + if not ret: + self._max_level = k + + elif k == -1: + ret = {v: M._from_dict({u: ZZ(len(Q.all_paths(u, v, use_multiedges=True))) for u in Q.vertex_iterator()}) + for v in Q.vertex_iterator()} + + elif k < -1: + prev = self._dim_vecs_level(k+1) + ret = {} + for v in self._top_sort: + if v not in prev: # assumption: this vertex will never reappear + continue + temp = -prev[v] + for _, u, _ in Q.outgoing_edge_iterator(v): + if u in prev: + temp += prev[u] + for u, _, _ in Q.incoming_edge_iterator(v): + if u in ret: + temp += ret[u] + if all(coeff > 0 for key, coeff in temp): + ret[v] = temp + + return ret + + def dimension_vectors_of_level(self, k): + r""" + Return a :class:`Family` of dimension vectors of level ``k``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR.dimension_vectors_of_level(1) + {1: v1, 2: v1 + v2 + v3, 3: v3, 4: v3 + v4} + sage: AR.dimension_vectors_of_level(3) + {1: v4, 3: v2} + sage: AR.dimension_vectors_of_level(10) + {} + sage: AR.dimension_vectors_of_level(-1) + {1: v1 + v2, 2: v2, 3: v2 + v3 + v4, 4: v4} + sage: AR.dimension_vectors_of_level(-2) + {1: v3 + v4, 2: v1 + v2 + v3 + v4, 3: v1 + v2 + v3, 4: v2 + v3} + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR.dimension_vectors_of_level(1) + {1: v1 + 2*v2, 2: v2} + sage: AR.dimension_vectors_of_level(3) + {1: 5*v1 + 6*v2, 2: 4*v1 + 5*v2} + sage: AR.dimension_vectors_of_level(-1) + {1: v1, 2: 2*v1 + v2} + sage: AR.dimension_vectors_of_level(-3) + {1: 5*v1 + 4*v2, 2: 6*v1 + 5*v2} + """ + ret = self._dim_vecs_level(k) + return dict(ret) # make a (shallow) copy to allow a user to mutate it + + class Element(Element): + r""" + A node in the AR quiver. + """ + def __init__(self, parent, vertex, level): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: TestSuite(AR(1, 3)).run() + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: TestSuite(AR(2, 3)).run() + sage: TestSuite(AR(1, -4)).run() + """ + self._vertex = vertex + self._level = ZZ(level) + Element.__init__(self, parent) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: AR(1, 3) + <1, 3> + """ + return f"<{self._vertex}, {self._level}>" + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(2, 2) + sage: latex(node) + \left\langle 2, 2 \right\rangle + sage: AR.options.latex = "dimension_vector" + sage: latex(node) + v_{2} + v_{3} + sage: AR.options.latex = "both" + sage: latex(node) + \begin{gathered} \left\langle 2, 2 \right\rangle \\ v_{2} + v_{3} \end{gathered} + sage: AR.options._reset() + """ + from sage.misc.latex import latex + node = r"\left\langle {}, {} \right\rangle".format(latex(self._vertex), self._level) + latex_option = self.parent().options.latex + if latex_option == "node": + return node + dim_vec = latex(self.dimension_vector()) + if latex_option == "dimension_vector": + return dim_vec + return r"\begin{{gathered}} {} \\ {} \end{{gathered}}".format(node, dim_vec) + + def _richcmp_(self, other, op): + r""" + Rich comparison of ``self`` to ``other`` by ``op``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: sorted(AR) + [<1, 1>, <2, 1>, <3, 1>, <1, 2>, <2, 2>, <3, 2>] + """ + return richcmp((self._level, self._vertex), (other._level, other._vertex), op) + + def __hash__(self): + r""" + Return the hash of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 2) + sage: hash(node) == hash((2, 1)) + True + """ + return hash((self._level, self._vertex)) + + def vertex(self): + r""" + Return the vertex of the quiver corresponding to ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 2) + sage: node.vertex() + 1 + """ + return self._vertex + + def level(self): + r""" + Return the level of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 2) + sage: node.level() + 2 + """ + return self._level + + def translation(self): + r""" + Return the AR translation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[4,3], [3,2], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 1) + sage: node.translation() is None + True + sage: node = AR(1, 2) + sage: node.translation() + <1, 1> + """ + if self._level == 1: + return None + dim_vecs = self.parent()._dim_vecs_level(self._level - 1) + if self._vertex not in dim_vecs: # this likely never happens + return None + return type(self)(self.parent(), self._vertex, self._level - 1) + + def inverse_translation(self): + r""" + Return the inverse AR translation of ``self``. + + EXAMPLES:: + + sage: DA = DiGraph([[2,3], [2,1]]) + sage: AR = DA.auslander_reiten_quiver() + sage: node = AR(1, 1) + sage: node.inverse_translation() + <1, 2> + sage: node = AR(1, 2) + sage: node.inverse_translation() is None + True + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: AR(2, -1).inverse_translation() is None + True + """ + if self._level == -1: + return None + dim_vecs = self.parent()._dim_vecs_level(self._level + 1) + if self._vertex not in dim_vecs: + return None + return type(self)(self.parent(), self._vertex, self._level + 1) + + def dimension_vector(self): + r""" + Return the dimension vector of ``self``. + + EXAMPLES:: + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: AR = D.auslander_reiten_quiver() + sage: node = AR(2, -4) + sage: node.dimension_vector() + 8*v1 + 7*v2 + """ + return self.parent()._dim_vecs_level(self._level)[self._vertex] + + +def detect_dynkin_quiver(quiver): + """ + Determine if ``quiver`` is a finite type Dynkin quiver. + + EXAMPLES:: + + sage: from sage.quivers.ar_quiver import detect_dynkin_quiver + sage: D = DiGraph([[1,2], [2,3], [3, 4], [4,0], ['a','b'], ['b','c'], ['c','d'], ['c','e']]) + sage: detect_dynkin_quiver(D) + D5xA5 + + sage: D = DiGraph([[1,2,'a'], [1,2,'b']], multiedges=True) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1, 2], [2, 3], [1, 3]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [1,3], [1,4], [1,5]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [2,3], [2,4], [4,5], [6,4]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [2,3], [3,4], [4,5], [5,6], [6,7], [7,8], [8,9], [0,3]]) + sage: detect_dynkin_quiver(D) is None + True + sage: D = DiGraph([[1,2], [2,3], [3,4], [4,5], [5,6], [6,7], [0,4]]) + sage: detect_dynkin_quiver(D) is None + True + """ + dynkin_type = [] + for Q in quiver.connected_components_subgraphs(): + if Q.has_multiple_edges(): + return None + G = Q.to_undirected() + if G.is_path(): + dynkin_type.append(['A', Q.num_verts()]) + continue + degthree = G.vertices(degree=3) + if len(degthree) != 1: + return None + G = G.copy(immutable=False) + G.delete_vertex(degthree[0]) + path_lengths = sorted(G.connected_components_sizes()) + if len(path_lengths) != 3: + return None + if path_lengths[:2] == [1, 1]: + dynkin_type.append(['D', G.num_verts() + 1]) + elif path_lengths[:2] == [1, 2] and path_lengths[2] in [2, 3, 4]: + dynkin_type.append(['E', G.num_verts() + 1]) + else: + return None + if len(dynkin_type) == 1: + return CartanType(dynkin_type[0]) + return CartanType(dynkin_type) From a0329f558a3601b5a16c3bf71a8ffec55ed5be28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chenevi=C3=A8re?= Date: Wed, 31 Jan 2024 11:23:49 +0100 Subject: [PATCH 058/191] mini modif --- src/sage/combinat/tamari_lattices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 43191540d71..8d8cd621d69 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -66,7 +66,7 @@ def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: - `a` and `b` -- coprime integers with `a \geq b` - `i` and `j` -- nonnegative integers with `1 \geq \frac{j}{b} \geq - \frac{bi}{a} \geq 0` + \frac{i}{a} \geq 0` OUTPUT: From 81eb2c340dadf5d264281462240bc6b05bc6badd Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sun, 3 Dec 2023 13:31:56 +0100 Subject: [PATCH 059/191] =?UTF-8?q?fast=20path=20for=20V=C3=A9lu=20isogeni?= =?UTF-8?q?es=20with=20a=20single=20kernel=20generator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../elliptic_curves/ell_curve_isogeny.py | 119 +++++++++++++----- 1 file changed, 90 insertions(+), 29 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 68e9c3fc79d..243213c2f80 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1074,7 +1074,7 @@ def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True self.__algorithm = algorithm if algorithm == 'velu': - self.__init_from_kernel_list(kernel) + self.__init_from_kernel_gens(kernel) elif algorithm == 'kohel': self.__init_from_kernel_polynomial(kernel) else: @@ -1870,7 +1870,7 @@ def __setup_post_isomorphism(self, codomain, model): # Setup function for Velu's formula # - def __init_from_kernel_list(self, kernel_gens): + def __init_from_kernel_gens(self, kernel_gens): r""" Private function that initializes the isogeny from a list of points which generate the kernel (For Vélu's formulas.) @@ -1884,7 +1884,7 @@ def __init_from_kernel_list(self, kernel_gens): Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7 - sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))]) + sage: phi._EllipticCurveIsogeny__init_from_kernel_gens([E(0), E((0,0))]) The following example demonstrates the necessity of avoiding any calls to P.order(), since such calls involve factoring the group order which @@ -1907,6 +1907,14 @@ def __init_from_kernel_list(self, kernel_gens): if not P.has_finite_order(): raise ValueError("given kernel contains point of infinite order") + self.__kernel_mod_sign = {} + self.__v = self.__w = 0 + + # Fast path: The kernel is given by a single generating point. + if len(kernel_gens) == 1 and kernel_gens[0]: + self.__init_from_kernel_point(kernel_gens[0]) + return + # Compute a list of points in the subgroup generated by the # points in kernel_gens. This is very naive: when finite # subgroups are implemented better, this could be simplified, @@ -1927,12 +1935,86 @@ def all_multiples(itr, terminal): self._degree = Integer(len(kernel_set)) self.__kernel_list = list(kernel_set) - self.__sort_kernel_list() + self.__init_from_kernel_list() # # Precompute the values in Velu's Formula. # - def __sort_kernel_list(self): + def __update_kernel_data(self, xQ, yQ): + r""" + Internal helper function to update some data coming from the + kernel points of this isogeny when using Vélu's formulas. + + TESTS: + + The following example inherently exercises this function:: + + sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) + sage: P = E((4,2)) + sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + """ + a1, a2, a3, a4, _ = self._domain.a_invariants() + + gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ + gyQ = -2*yQ - a1*xQ - a3 + + uQ = gyQ**2 + + if 2*yQ == -a1*xQ - a3: # Q is 2-torsion + vQ = gxQ + else: # Q is not 2-torsion + vQ = 2*gxQ - a1*gyQ + + self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ + + self.__v += vQ + self.__w += uQ + xQ*vQ + + def __init_from_kernel_point(self, ker): + r""" + Private function with functionality equivalent to + :meth:`__init_from_kernel_list`, but optimized for when + the kernel is given by a single point. + + TESTS: + + The following example inherently exercises this function:: + + sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) + sage: P = E((4,2)) + sage: phi = EllipticCurveIsogeny(E, P); phi # implicit doctest + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + + We check that the result is the same as for :meth:`__init_from_kernel_list`:: + + sage: psi = EllipticCurveIsogeny(E, [P, P]); psi + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + sage: phi == psi + True + """ + self._degree = Integer(1) + + Q, prevQ = ker, self._domain(0) + + while Q and Q != -prevQ: + self.__update_kernel_data(*Q.xy()) + + if Q == -Q: + self._degree += 1 + break + + prevQ = Q + Q += ker + self._degree += 2 + + def __init_from_kernel_list(self): r""" Private function that sorts the list of points in the kernel (For Vélu's formulas). Sorts out the 2-torsion points, and @@ -1944,43 +2026,22 @@ def __sort_kernel_list(self): sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) sage: P = E((4,2)) - sage: phi = EllipticCurveIsogeny(E, P); phi + sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 - sage: phi._EllipticCurveIsogeny__sort_kernel_list() """ - a1, a2, a3, a4, _ = self._domain.a_invariants() - - self.__kernel_mod_sign = {} - v = w = 0 - for Q in self.__kernel_list: if Q.is_zero(): continue - xQ,yQ = Q.xy() + xQ, yQ = Q.xy() if xQ in self.__kernel_mod_sign: continue - gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ - gyQ = -2*yQ - a1*xQ - a3 - - uQ = gyQ**2 - - if 2*yQ == -a1*xQ - a3: # Q is 2-torsion - vQ = gxQ - else: # Q is not 2-torsion - vQ = 2*gxQ - a1*gyQ - - self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ - - v += vQ - w += uQ + xQ*vQ - - self.__v, self.__w = v, w + self.__update_kernel_data(xQ, yQ) # # Velu's formula computing the codomain curve From 04824e35f3a479159debe211d62ec0d5cfdad68b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 31 Jan 2024 11:50:43 +0100 Subject: [PATCH 060/191] make graded depend on supplying degrees --- src/sage/algebras/free_algebra.py | 64 ++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index 427fa8175d0..8b7477d79c5 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -163,11 +163,13 @@ from sage.misc.lazy_import import lazy_import from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.ring import Algebra -from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis +from sage.rings.integer_ring import ZZ +from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.words.word import Word from sage.structure.category_object import normalize_names + lazy_import('sage.algebras.letterplace.free_algebra_letterplace', 'FreeAlgebra_letterplace') @@ -330,7 +332,9 @@ def create_key(self, base_ring, arg1=None, arg2=None, if arg2 is None: arg2 = len(arg1) names = normalize_names(arg2, arg1) - return base_ring, names + if degrees is None: + return base_ring, names + return base_ring, names, tuple(degrees) def create_object(self, version, key): """ @@ -354,7 +358,9 @@ def create_object(self, version, key): if isinstance(key[0], tuple): from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace return FreeAlgebra_letterplace(key[1], degrees=key[0]) - return FreeAlgebra_generic(key[0], len(key[1]), key[1]) + if len(key) == 2: + return FreeAlgebra_generic(key[0], len(key[1]), key[1]) + return FreeAlgebra_generic(key[0], len(key[1]), key[1], key[2]) FreeAlgebra = FreeAlgebraFactory('FreeAlgebra') @@ -391,6 +397,9 @@ class FreeAlgebra_generic(CombinatorialFreeModule, Algebra): - ``R`` -- a ring - ``n`` -- an integer - ``names`` -- the generator names + - ``degrees`` -- (optional) a tuple or list specifying the + degrees of all the generators, if omitted, the algebra is not + graded EXAMPLES:: @@ -442,10 +451,11 @@ class FreeAlgebra_generic(CombinatorialFreeModule, Algebra): implementation of free algebras. Two corresponding free algebras in different implementations are not equal, but there is a coercion. + """ Element = FreeAlgebraElement - def __init__(self, R, n, names): + def __init__(self, R, n, names, degrees=None): """ The free algebra on `n` generators over a base ring. @@ -467,10 +477,22 @@ def __init__(self, R, n, names): raise TypeError("argument R must be a ring") self.__ngens = n indices = FreeMonoid(n, names=names) - cat = GradedAlgebrasWithBasis(R) + if degrees is None: + cat = AlgebrasWithBasis(R) + else: + if (len(degrees) == len(names) and + all(d in ZZ for d in degrees)): + cat = AlgebrasWithBasis(R).Graded() + else: + raise ValueError("argument degrees must specify an integer for each generator") + CombinatorialFreeModule.__init__(self, R, indices, prefix='F', category=cat) self._assign_names(indices.variable_names()) + if degrees is None: + self._degrees = None + else: + self._degrees = {g: d for g, d in zip(self.monoid().gens(), degrees)} def one_basis(self): """ @@ -527,17 +549,23 @@ def _repr_(self) -> str: EXAMPLES:: - sage: F = FreeAlgebra(QQ,3,'x') - sage: F # indirect doctest + sage: F = FreeAlgebra(QQ, 3, 'x') + sage: F # indirect doctest Free Algebra on 3 generators (x0, x1, x2) over Rational Field sage: F.rename('QQ<>') - sage: F #indirect doctest + sage: F # indirect doctest QQ<> - sage: FreeAlgebra(ZZ,1,['a']) + sage: FreeAlgebra(ZZ, 1, ['a']) Free Algebra on 1 generators (a,) over Integer Ring + + sage: FreeAlgebra(QQ, 2, ['x', 'y'], degrees=(2,1)) + Free Algebra on 2 generators (x, y) with degrees (2, 1) over Rational Field """ - return "Free Algebra on {} generators {} over {}".format( - self.__ngens, self.gens(), self.base_ring()) + if self._degrees is None: + return "Free Algebra on {} generators {} over {}".format( + self.__ngens, self.gens(), self.base_ring()) + return "Free Algebra on {} generators {} with degrees {} over {}".format( + self.__ngens, self.gens(), tuple(self._degrees.values()), self.base_ring()) def _latex_(self) -> str: r""" @@ -780,16 +808,16 @@ def degree_on_basis(self, m): EXAMPLES:: - sage: A. = FreeAlgebra(QQ) - sage: m = A.basis().keys()[10] + sage: A. = FreeAlgebra(QQ, degrees=(1, -1)) + sage: m = A.basis().keys()[42] sage: m - a*b^2 + a*b*a*b^2 sage: A.degree_on_basis(m) - 3 - sage: (a*b^2).degree() - 3 + -1 + sage: (a*b*a*b^2).degree() + -1 """ - return len(m) + return sum(self._degrees[g] * e for g, e in m) def product_on_basis(self, x, y): """ From 34008e21445542d01f864c97f43c2a268241f825 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 31 Jan 2024 17:08:35 +0100 Subject: [PATCH 061/191] allow single integer as degree and fix code --- src/sage/algebras/free_algebra.py | 4 +++- src/sage/algebras/free_algebra_element.py | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index 8b7477d79c5..be7de314ab4 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -334,6 +334,8 @@ def create_key(self, base_ring, arg1=None, arg2=None, names = normalize_names(arg2, arg1) if degrees is None: return base_ring, names + if degrees in ZZ: + return base_ring, names, (degrees,) * len(names) return base_ring, names, tuple(degrees) def create_object(self, version, key): @@ -1194,7 +1196,7 @@ def __init__(self, alg): """ R = alg.base_ring() self._alg = alg - category = GradedAlgebrasWithBasis(R) + category = AlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, alg.monoid(), prefix='PBW', category=category) self._assign_names(alg.variable_names()) diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index e30e5f7d917..8b8b23b3a8e 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -209,15 +209,20 @@ def is_unit(self): EXAMPLES:: - sage: A. = FreeAlgebra(ZZ) + sage: A. = FreeAlgebra(ZZ) sage: A(-1).is_unit() True sage: A(2).is_unit() False sage: A(1 + x).is_unit() False + sage: A. = FreeAlgebra(QQ, degrees=(1,-1)) + sage: A(x * y).is_unit() + False + sage: A(2).is_unit() + True """ - if self.is_zero() or not self.is_homogeneous() or self.degree(): + if self.is_zero() or len(self) > 1 or not list(self)[0][0].is_one(): return False c = self.leading_coefficient() return c.is_unit() @@ -226,7 +231,7 @@ def __invert__(self): """ EXAMPLES:: - sage: A. = FreeAlgebra(QQ) + sage: A. = FreeAlgebra(QQ) sage: ~A(1) 1 From a244120ff29ba9e5d0a32c75a4ba0df7f41722c1 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 31 Jan 2024 17:43:09 +0100 Subject: [PATCH 062/191] allow several arguments in subs, allow indices as keys in Laurent polynomials --- src/sage/rings/fraction_field_FpT.pyx | 4 ++-- src/sage/rings/fraction_field_element.pyx | 9 ++++----- src/sage/rings/polynomial/laurent_polynomial_mpair.pyx | 10 ++++++++-- src/sage/rings/polynomial/polynomial_element.pyx | 8 ++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index f2c35b8eb0a..ac833a196b4 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -268,7 +268,7 @@ cdef class FpTElement(FieldElement): """ return self.numer()(*args, **kwds) / self.denom()(*args, **kwds) - def subs(self, in_dict=None, **kwds): + def subs(self, in_dict=None, *args, **kwds): """ EXAMPLES:: @@ -280,7 +280,7 @@ cdef class FpTElement(FieldElement): sage: f.subs(X=2) (t + 1)/(t + 10) """ - return self.numer().subs(in_dict, **kwds) / self.denom().subs(in_dict, **kwds) + return self.numer().subs(in_dict, *args, **kwds) / self.denom().subs(in_dict, *args, **kwds) def valuation(self, v): """ diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 15e6087b1fa..a43a827e240 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -446,7 +446,7 @@ cdef class FractionFieldElement(FieldElement): """ return self._numerator(*x, **kwds) / self._denominator(*x, **kwds) - def subs(self, in_dict=None, **kwds): + def subs(self, in_dict=None, *args, **kwds): r""" Substitute variables in the numerator and denominator of ``self``. @@ -472,9 +472,8 @@ cdef class FractionFieldElement(FieldElement): sage: v = P.gen(4) sage: p.subs({v: 100}) (x1 + 2*x2 + 3*x3 + 400)/(x1 + 2*x2 + 3*x3 + 5*x5 + 6*x6 + 7*x7 + 400) - """ - if in_dict: + if isinstance(in_dict, dict): gens = self.parent().gens() def to_R(m): @@ -485,8 +484,8 @@ cdef class FractionFieldElement(FieldElement): return mi in_dict = {to_R(m): v for m, v in in_dict.items()} - num = self._numerator.subs(in_dict, **kwds) - den = self._denominator.subs(in_dict, **kwds) + num = self._numerator.subs(in_dict, *args, **kwds) + den = self._denominator.subs(in_dict, *args, **kwds) return num / den substitute = subs diff --git a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx index 7832488a2ef..893b81c87af 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx @@ -1345,14 +1345,20 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: x.subs({x: 2}, x=1) 1 + + sage: f.subs({1: 2}, x=1) + 3*z + 5 """ cdef list variables = list(self._parent.gens()) cdef Py_ssize_t i for i in range(len(variables)): if str(variables[i]) in kwds: variables[i] = kwds[str(variables[i])] - elif in_dict and variables[i] in in_dict: - variables[i] = in_dict[variables[i]] + elif in_dict: + if variables[i] in in_dict: + variables[i] = in_dict[variables[i]] + elif i in in_dict: + variables[i] = in_dict[i] return self(tuple(variables)) def is_constant(self): diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index dde8a42123a..853b6e62780 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -410,7 +410,7 @@ cdef class Polynomial(CommutativePolynomial): return self._parent.zero() return self * self._parent(right) - def subs(self, in_dict=None, **kwds): + def subs(self, in_dict=None, *args, **kwds): r""" Substitute the variable in ``self``. @@ -432,17 +432,17 @@ cdef class Polynomial(CommutativePolynomial): TypeError: keys do not match self's parent """ if not in_dict: - return self(**kwds) + return self(*args, **kwds) if isinstance(in_dict, dict): if len(in_dict) > 1: raise TypeError("only the generator can be substituted, use __call__ instead") k, v = next(iter(in_dict.items())) if not k or k == self._parent.gen(): - return self(v, **kwds) + return self(v, *args, **kwds) raise TypeError("keys do not match self's parent") - return self(in_dict, **kwds) + return self(in_dict, *args, **kwds) substitute = subs From a1105b5cfc8cd4840cc24ff19ad0a05e80556e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chenevi=C3=A8re?= Date: Wed, 31 Jan 2024 18:27:38 +0100 Subject: [PATCH 063/191] added the possibility of rational slopes in Generalized Tamari lattices, and update references --- src/doc/en/reference/references/index.rst | 8 ++++ src/sage/combinat/tamari_lattices.py | 46 ++++++++++++----------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 1c075a474d6..335bc1233f8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -508,6 +508,11 @@ REFERENCES: Springer, 2012, http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf +.. [BMFPR2011] \M. Bousquet-Melou, É. Fusy, L.-F. Préville Ratelle, + *The number of intervals in the m-Tamari lattices*. + Electronic Journal of Combinatorics 18(2), 2011. + :doi:`10.37236/2027` + .. [BPPSST2017] Banik, Pandey, Peyrin, Sasaki, Sim, and Todo, GIFT : A Small Present Towards Reaching the Limit of Lightweight Encryption. *Cryptographic Hardware and Embedded Systems - CHES 2017*, @@ -1426,6 +1431,9 @@ REFERENCES: *Omitting parentheses from the cyclic notation*. (2013). :arxiv:`1308.0936v2`. +.. [CC2023] \C. Ceballos and C. Chenevière. + *On linear intervals in the alt `\nu`-Tamari lattices* :arxiv:`2305.02250` + .. [CCL2015] \N. Cohen, D. Coudert, and A. Lancin. *On computing the Gromov hyperbolicity*. ACM Journal of Experimental Algorithmics, 20(1.6):1-18, 2015. :doi:`10.1145/2780652` or diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 8d8cd621d69..997ca034890 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -3,7 +3,7 @@ Generalized Tamari lattices These lattices depend on three parameters `a`, `b` and `m`, where `a` -and `b` are coprime positive integers and `m` is a nonnegative +and `b` are positive integers and `m` is a nonnegative integer. The elements are :func:`Dyck paths` @@ -48,8 +48,6 @@ # **************************************************************************** from __future__ import annotations from sage.combinat.posets.lattices import LatticePoset, MeetSemilattice -from sage.arith.misc import gcd - def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: r""" @@ -63,7 +61,7 @@ def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: INPUT: - - `a` and `b` -- coprime integers with `a \geq b` + - `a` and `b` -- integers with `a \geq b` - `i` and `j` -- nonnegative integers with `1 \geq \frac{j}{b} \geq \frac{i}{a} \geq 0` @@ -103,7 +101,7 @@ def paths_in_triangle(i, j, a, b) -> list[tuple[int, ...]]: def swap(p, i, m=1) -> tuple[int, ...]: r""" - Perform a covering move in the `(a,b)`-Tamari lattice of parameter `m`. + Perform a covering move in the `(a,b)`-Tamari lattice of slope parameter `m`. The letter at position `i` in `p` must be a `0`, followed by at least one `1`. @@ -125,6 +123,11 @@ def swap(p, i, m=1) -> tuple[int, ...]: (1, 1, 0, 0, 0) sage: swap((1,1,0,0,1,1,0,0,0),3) (1, 1, 0, 1, 1, 0, 0, 0, 0) + sage: swap((1,0,1,0,1,0,0,0), 1, 1) + (1, 1, 0, 0, 1, 0, 0, 0) + sage: swap((1,0,1,0,1,0,0,0), 1, 5/3) + (1, 1, 0, 1, 0, 0, 0, 0) + TESTS:: @@ -151,7 +154,7 @@ def swap(p, i, m=1) -> tuple[int, ...]: height += m else: height -= 1 - if height == 0: + if height <= 0: found = True q = [k for k in p] for k in range(i, j): @@ -160,19 +163,19 @@ def swap(p, i, m=1) -> tuple[int, ...]: return tuple(q) -def GeneralizedTamariLattice(a, b, m=1, check=True): +def GeneralizedTamariLattice(a, b, m=1): r""" Return the `(a,b)`-Tamari lattice of parameter `m`. INPUT: - - `a` and `b` -- coprime integers with `a \geq b` + - `a` and `b` -- integers with `a \geq b` - - `m` -- a nonnegative integer such that `a \geq b m` + - `m` -- a nonnegative rational number such that `a \geq b m` OUTPUT: - - a finite lattice (the lattice property is only conjectural in general) + - a finite lattice (special case of the alt `\nu`-Tamari lattices in [CC2023]_) The elements of the lattice are :func:`Dyck paths` in the @@ -185,7 +188,8 @@ def GeneralizedTamariLattice(a, b, m=1, check=True): The usual :wikipedia:`Tamari lattice` of index `b` is the special case `a=b+1` and `m=1`. - Other special cases give the `m`-Tamari lattices studied in [BMFPR]_. + Other special cases give the `m`-Tamari lattices studied in [BMFPR2011]_, + or the rational Tamari lattices when a and b are coprime and m = a/b (see [PRV2017]_). EXAMPLES:: @@ -194,17 +198,16 @@ def GeneralizedTamariLattice(a, b, m=1, check=True): Finite lattice containing 2 elements sage: GeneralizedTamariLattice(4,3) Finite lattice containing 5 elements - sage: GeneralizedTamariLattice(4,4) - Traceback (most recent call last): - ... - ValueError: the numbers a and b must be coprime with a>=b sage: GeneralizedTamariLattice(7,5,2) Traceback (most recent call last): ... ValueError: the condition a>=b*m does not hold - sage: P = GeneralizedTamariLattice(5,3);P + sage: P = GeneralizedTamariLattice(5,3); P + Finite lattice containing 7 elements + sage: P = GeneralizedTamariLattice(5, 3, m=5/3); P Finite lattice containing 7 elements + TESTS:: sage: P.coxeter_transformation()**18 == 1 # needs sage.libs.flint @@ -212,11 +215,10 @@ def GeneralizedTamariLattice(a, b, m=1, check=True): REFERENCES: - .. [BMFPR] \M. Bousquet-Melou, E. Fusy, L.-F. Preville Ratelle. - *The number of intervals in the m-Tamari lattices*. :arxiv:`1106.1498` + [BMFPR2011]_ + [PRV2017]_ + [CC2023]_ """ - if not (gcd(a, b) == 1 and a >= b): - raise ValueError("the numbers a and b must be coprime with a>=b") if a < b * m: raise ValueError("the condition a>=b*m does not hold") @@ -224,7 +226,7 @@ def covers(p): return [swap(p, i, m) for i in range(len(p) - 1) if not p[i] and p[i + 1]] return LatticePoset({p: covers(p) - for p in paths_in_triangle(a, b, a, b)}, check=check) + for p in paths_in_triangle(a, b, a, b)}, check=False) def TamariLattice(n, m=1): @@ -263,7 +265,7 @@ def TamariLattice(n, m=1): - [BMFPR]_ """ - return GeneralizedTamariLattice(m * n + 1, n, m, check=False) + return GeneralizedTamariLattice(m * n + 1, n, m) # a variation : the Dexter meet-semilattices From e9c13208f42cac8519e8fa5833acfb1974d8b021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chenevi=C3=A8re?= Date: Wed, 31 Jan 2024 18:54:45 +0100 Subject: [PATCH 064/191] fix ref --- src/sage/combinat/tamari_lattices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 997ca034890..7f7b217c5d0 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -263,7 +263,7 @@ def TamariLattice(n, m=1): REFERENCES: - - [BMFPR]_ + - [BMFPR2011]_ """ return GeneralizedTamariLattice(m * n + 1, n, m) From 58c33779239a10514b3409a840e59e1c6b7e15af Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 31 Jan 2024 19:08:01 +0100 Subject: [PATCH 065/191] unify alias substitute for subs --- src/sage/interfaces/macaulay2.py | 4 +- src/sage/rings/asymptotic/asymptotic_ring.py | 3 +- src/sage/rings/fraction_field_element.pyx | 2 - .../rings/polynomial/polynomial_element.pyx | 2 - src/sage/structure/element.pyx | 53 ++++++++----------- src/sage/symbolic/expression.pyx | 4 +- 6 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index fde9556858d..0125a2273db 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -1241,7 +1241,7 @@ def structure_sheaf(self): deprecation(27848, 'The function `structure_sheaf` is deprecated. Use `self.sheaf()` instead.') return self.parent()('OO_%s' % self.name()) - def substitute(self, *args, **kwds): + def subs(self, *args, **kwds): """ Note that we have to override the substitute method so that we get the default one from Macaulay2 instead of the one provided by Element. @@ -1260,8 +1260,6 @@ def substitute(self, *args, **kwds): """ return self.__getattr__("substitute")(*args, **kwds) - subs = substitute - def _tab_completion(self): """ Return a list of tab completions for ``self``. diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index d4cd2446e36..867fee0bfcf 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -2476,7 +2476,7 @@ def exp(self, precision=None): """ return self.rpow('e', precision=precision) - def substitute(self, rules=None, domain=None, **kwds): + def subs(self, rules=None, domain=None, **kwds): r""" Substitute the given ``rules`` in this asymptotic expansion. @@ -2700,7 +2700,6 @@ def substitute(self, rules=None, domain=None, **kwds): TypeError('Cannot apply the substitution rules %s on %s ' 'in %s.' % (rules, self, self.parent())), e) - subs = substitute def _substitute_(self, rules): r""" diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index a43a827e240..1ca503f12d0 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -488,8 +488,6 @@ cdef class FractionFieldElement(FieldElement): den = self._denominator.subs(in_dict, *args, **kwds) return num / den - substitute = subs - def _is_atomic(self): """ EXAMPLES:: diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 853b6e62780..4a5e51d1f6c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -444,8 +444,6 @@ cdef class Polynomial(CommutativePolynomial): return self(in_dict, *args, **kwds) - substitute = subs - @cython.boundscheck(False) @cython.wraparound(False) def __call__(self, *args, **kwds): diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index f1cf9d26ac7..56a28643905 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -827,6 +827,28 @@ cdef class Element(SageObject): variables.append(gen) return self(*variables) + def substitute(self, *args, **kwds): + """ + This calls :meth:`self.subs`. + + EXAMPLES:: + + sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() + sage: f = x^2 + y + x^2*y^2 + 5 + sage: f((5,y)) + 25*y^2 + y + 30 + sage: f.substitute({x: 5}) + 25*y^2 + y + 30 + sage: f.substitute(x=5) + 25*y^2 + y + 30 + sage: (1/f).substitute(x=5) + 1/(25*y^2 + y + 30) + sage: Integer(5).substitute(x=4) + 5 + """ + return self.subs(*args, **kwds) + + def numerical_approx(self, prec=None, digits=None, algorithm=None): """ Return a numerical approximation of ``self`` with ``prec`` bits @@ -906,37 +928,6 @@ cdef class Element(SageObject): """ return self.n(prec)._mpmath_(prec=prec) - def substitute(self,in_dict=None,**kwds): - """ - This is an alias for self.subs(). - - INPUT: - - - ``in_dict`` - (optional) dictionary of inputs - - - ``**kwds`` - named parameters - - OUTPUT: - - - new object if substitution is possible, otherwise self. - - EXAMPLES:: - - sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens() - sage: f = x^2 + y + x^2*y^2 + 5 - sage: f((5,y)) - 25*y^2 + y + 30 - sage: f.substitute({x: 5}) - 25*y^2 + y + 30 - sage: f.substitute(x=5) - 25*y^2 + y + 30 - sage: (1/f).substitute(x=5) - 1/(25*y^2 + y + 30) - sage: Integer(5).substitute(x=4) - 5 - """ - return self.subs(in_dict,**kwds) - cpdef _act_on_(self, x, bint self_on_left) noexcept: """ Use this method to implement ``self`` acting on ``x``. diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 345ee9b713b..ab19b46c181 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5574,7 +5574,7 @@ cdef class Expression(Expression_abc): cdef Expression p = self.coerce_in(pattern) return self._gobj.has(p._gobj) - def substitute(self, *args, **kwds): + def subs(self, *args, **kwds): """ Substitute the given subexpressions in this expression. @@ -5899,8 +5899,6 @@ cdef class Expression(Expression_abc): res = self._gobj.subs_map(smap, 0) return new_Expression_from_GEx(self._parent, res) - subs = substitute - cpdef Expression _subs_expr(self, expr) noexcept: """ EXAMPLES:: From d71d37a28d3cfe35fda55b32c2fe5305b2e858ff Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 31 Jan 2024 19:31:09 +0100 Subject: [PATCH 066/191] remove bad blank line --- src/sage/rings/asymptotic/asymptotic_ring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 867fee0bfcf..7bb25a6748d 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -2700,7 +2700,6 @@ def subs(self, rules=None, domain=None, **kwds): TypeError('Cannot apply the substitution rules %s on %s ' 'in %s.' % (rules, self, self.parent())), e) - def _substitute_(self, rules): r""" Substitute the given ``rules`` in this asymptotic expansion. From 450b0cd983fd74269f41ae1431a9a6941bb17070 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Wed, 31 Jan 2024 22:10:54 +0000 Subject: [PATCH 067/191] More comments and refactoring of any_root --- .../rings/polynomial/polynomial_element.pyx | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index d4ebbf5702d..e2742581a1c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2416,7 +2416,7 @@ cdef class Polynomial(CommutativePolynomial): # But if any degree is allowed then there should certainly be a factor if self has degree > 0 raise AssertionError(f"no irreducible factor was computed for {self}. Bug.") - def any_root(self, ring=None, degree=None, assume_squarefree=False): + def any_root(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ Return a root of this polynomial in the given ring. @@ -2436,6 +2436,15 @@ cdef class Polynomial(CommutativePolynomial): finite fields. If ``True``, this polynomial is assumed to be squarefree. + - ``assume_distinct_deg`` (bool) -- Used for polynomials over + finite fields. If ``True``, all factors of this polynomial + are assumed to have degree ``degree``. + + .. WARNING:: + + Negative degree input will be deprecated. Instead use + ``assume_distinct_deg``. + EXAMPLES:: sage: # needs sage.rings.finite_rings @@ -2541,9 +2550,8 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: polynomial x^2 + 1 has no roots """ - # When not working over a finite field, do the simple - # thing of factoring for roots and picking the first - # root. If none available, raise an error. + # When not working over a finite field, do the simplecthing of factoring for + # roots and picking the first root. If none are available, raise an error. from sage.categories.finite_fields import FiniteFields if not self.base_ring() in FiniteFields(): rs = self.roots(ring=ring, multiplicities=False) @@ -2551,7 +2559,7 @@ cdef class Polynomial(CommutativePolynomial): return rs[0] raise ValueError(f"polynomial {self} has no roots") - # Look for a linear factor, if there is none, raise a ValueError + # When the degree is none, we only look for a linear factor if degree is None: # if a ring is given try and coerce the polynomial into this ring if ring is not None: @@ -2562,7 +2570,7 @@ cdef class Polynomial(CommutativePolynomial): # try and find a linear irreducible polynomial from f to compute a root try: - f = self.any_irreducible_factor(degree=ZZ(1), assume_squarefree=assume_squarefree) + f = self.any_irreducible_factor(degree=1, assume_squarefree=assume_squarefree) except ValueError: raise ValueError(f"no root of polynomial {self} can be computed") @@ -2571,8 +2579,10 @@ cdef class Polynomial(CommutativePolynomial): # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and # ensure that the degree is positive. - assume_distinct_deg = False + degree = ZZ(degree) if degree < 0: + from sage.misc.superseded import deprecation + deprecation(37170, "negative ``degree`` will be disallowed. Instead use the bool `assume_distinct_deg`.") degree = ZZ(abs(degree)) assume_distinct_deg = True @@ -2581,7 +2591,9 @@ cdef class Polynomial(CommutativePolynomial): # if the degree and a ring is given however, instead compute a degree `degree` factor in the # base ring and then find a factor from this in the supplied ring. try: - f = self.any_irreducible_factor(degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) + f = self.any_irreducible_factor(degree=degree, + assume_squarefree=assume_squarefree, + assume_distinct_deg=assume_distinct_deg) except ValueError: raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") @@ -2592,25 +2604,24 @@ cdef class Polynomial(CommutativePolynomial): return root return ring(root) - # If the user asks for a specific ring, find the root in this ring from - # the polynomial of user supplied degree `degree` - if ring is not None: - try: - f = f.change_ring(ring) - except ValueError: - raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") - return f.any_root() - - # Otherwise compute an extension ring of the correct degree and - # compute a root - # TODO: a faster option would be to create an extension with `f` - # as F_ext = self.base_ring().extension(f, names="a") - # however this returns a quotient ring rather than a - # FiniteField type if the base field is a non-prime field, - # and this slower option is chosen to ensure the root is - # over explicitly a FiniteField type. - F_ext = self.base_ring().extension(f.degree(), names="a") - f = f.change_ring(F_ext) + # We are now in the case where the irreducible polynomial we have found is + # of degree > 1. The old version of this function simply computed the roots + # of this by calling f.roots(ring)... I don't really understand why + # though, as we can simply ask for f.any_root() for this polynomial over the + # new ring? + if ring is None: + # TODO: a faster option would be to create an extension with `f` + # as F_ext = self.base_ring().extension(f, names="a") + # however this returns a quotient ring rather than a + # FiniteField type if the base field is a non-prime field, + # so this slower option is chosen to ensure the root is + # over explicitly a FiniteField type. + ring = self.base_ring().extension(f.degree(), names="a") + + # Now we look for a linear root of this irreducible polynomial of degree `degree` + # over the user supplied ring or the extension we just computed. If the user sent + # a bad ring here of course there may be no root found. + f = f.change_ring(ring) return f.any_root() def __truediv__(left, right): From 6b6758267d32ee4508346183bd01fc6acac4bf14 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Wed, 31 Jan 2024 22:11:18 +0000 Subject: [PATCH 068/191] Modify doctests to handle that any_root() is no longer deterministic --- .../rings/finite_rings/conway_polynomials.py | 24 +++++------ .../rings/finite_rings/hom_finite_field.pyx | 42 +++++-------------- .../finite_rings/hom_finite_field_givaro.pyx | 20 +++------ .../polynomial/polynomial_quotient_ring.py | 2 +- src/sage/schemes/elliptic_curves/cm.py | 2 +- src/sage/schemes/elliptic_curves/ell_field.py | 4 +- 6 files changed, 32 insertions(+), 62 deletions(-) diff --git a/src/sage/rings/finite_rings/conway_polynomials.py b/src/sage/rings/finite_rings/conway_polynomials.py index 02779f49bca..fcb3e2ab8d6 100644 --- a/src/sage/rings/finite_rings/conway_polynomials.py +++ b/src/sage/rings/finite_rings/conway_polynomials.py @@ -136,7 +136,7 @@ class PseudoConwayLattice(WithEqualityById, SageObject): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + x + 1 TESTS:: @@ -164,16 +164,16 @@ def __init__(self, p, use_database=True): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(3) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + 2*x + 1 sage: # needs sage.rings.finite_rings sage: PCL = PseudoConwayLattice(5, use_database=False) - sage: PCL.polynomial(12) + sage: PCL.polynomial(12) # random x^12 + 4*x^11 + 2*x^10 + 4*x^9 + 2*x^8 + 2*x^7 + 4*x^6 + x^5 + 2*x^4 + 2*x^2 + x + 2 - sage: PCL.polynomial(6) + sage: PCL.polynomial(6) # random x^6 + x^5 + 4*x^4 + 3*x^3 + 3*x^2 + 2*x + 2 - sage: PCL.polynomial(11) + sage: PCL.polynomial(11) # random x^11 + x^6 + 3*x^3 + 4*x + 3 """ self.p = p @@ -215,11 +215,11 @@ def polynomial(self, n): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + x + 1 - sage: PCL.polynomial(4) + sage: PCL.polynomial(4) # random x^4 + x^3 + 1 - sage: PCL.polynomial(60) + sage: PCL.polynomial(60) # random x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: @@ -239,7 +239,7 @@ def polynomial(self, n): # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } - xi = {q: self.polynomial(n//q).any_root(K, -n//q, assume_squarefree=True) + xi = {q: self.polynomial(n//q).any_root(K, n//q, assume_squarefree=True, assume_distinct_deg=True) for q in n.prime_divisors()} # The following is needed to ensure that in the concrete instantiation @@ -402,9 +402,9 @@ def _frobenius_shift(K, generators, check_only=False): sage: f20 = x^20 + x^19 + x^15 + x^13 + x^12 + x^11 + x^9 + x^8 + x^7 + x^4 + x^2 + x + 1 sage: f12 = x^12 + x^10 + x^9 + x^8 + x^4 + x^2 + 1 sage: K. = GF(2^60, modulus='first_lexicographic') - sage: x30 = f30.any_root(K) - sage: x20 = f20.any_root(K) - sage: x12 = f12.any_root(K) + sage: x30 = f30.roots(K, multiplicities=False)[0] + sage: x20 = f20.roots(K, multiplicities=False)[0] + sage: x12 = f12.roots(K, multiplicities=False)[0] sage: generators = {2: x30, 3: x20, 5: x12} sage: from sage.rings.finite_rings.conway_polynomials import _frobenius_shift, _find_pow_of_frobenius sage: _frobenius_shift(K, generators) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 85dadd6fb2c..8f38408cda9 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -16,32 +16,25 @@ Construction of an embedding:: sage: k. = GF(3^7) sage: K. = GF(3^21) - sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: f(t) + sage: f(t) # random T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T The map `f` has a method ``section`` which returns a partially defined map which is the inverse of `f` on the image of `f`:: - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) + sage: g(f(t^3+t^2+1)) # random t^3 + t^2 + 1 - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^7 - To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T There is no embedding of `GF(5^6)` into `GF(5^11)`:: @@ -132,14 +125,6 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: g = f.section() sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 - - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^7 - To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T """ for root, _ in x.minimal_polynomial().roots(ring=self.codomain()): if self._inverse(root) == x: @@ -158,7 +143,7 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g._repr_() + sage: g._repr_() # random 'Section of Ring morphism:\n From: Finite Field in t of size 3^7\n To: Finite Field in T of size 3^21\n Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T' """ return "Section of %s" % self._inverse @@ -202,7 +187,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: from sage.rings.finite_rings.hom_finite_field import FiniteFieldHomomorphism_generic sage: k. = GF(3^7) sage: K. = GF(3^21) - sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 @@ -299,7 +284,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(3^3) sage: K. = GF(3^9) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) - sage: f(t) + sage: f(t) # random 2*T^6 + 2*T^4 + T^2 + T sage: a = k.random_element() @@ -368,20 +353,13 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(3^7) sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^7 - To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T """ if self.base_map() is not None: raise NotImplementedError @@ -751,14 +729,14 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: kfixed, embed = f.fixed_field() sage: kfixed Finite Field in t_fixed of size 5^2 - sage: embed + sage: embed # random Ring morphism: From: Finite Field in t_fixed of size 5^2 To: Finite Field in t of size 5^6 Defn: t_fixed |--> 4*t^5 + 2*t^4 + 4*t^2 + t sage: tfixed = kfixed.gen() - sage: embed(tfixed) + sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t """ if self._degree_fixed == 1: diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index bc685731610..87281d41c64 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -59,7 +59,7 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: k. = GF(3^2) sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 @@ -105,16 +105,8 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) sage: g = f.section() - sage: g(f(t+1)) + sage: g(f(t+1)) # random t + 1 - - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^2 - To: Finite Field in T of size 3^4 - Defn: t |--> 2*T^3 + 2*T^2 + 1 """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) @@ -140,7 +132,7 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: from sage.rings.finite_rings.hom_finite_field_givaro import FiniteFieldHomomorphism_givaro sage: k. = GF(3^2) sage: K. = GF(3^4) - sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 @@ -182,7 +174,7 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: k. = GF(3^2) sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) - sage: f(t) + sage: f(t) # random 2*T^3 + 2*T^2 + 1 """ if x.parent() != self.domain(): @@ -242,14 +234,14 @@ cdef class FrobeniusEndomorphism_givaro(FrobeniusEndomorphism_finite_field): sage: kfixed, embed = f.fixed_field() sage: kfixed Finite Field in t_fixed of size 5^2 - sage: embed + sage: embed # random Ring morphism: From: Finite Field in t_fixed of size 5^2 To: Finite Field in t of size 5^6 Defn: t_fixed |--> 4*t^5 + 2*t^4 + 4*t^2 + t sage: tfixed = kfixed.gen() - sage: embed(tfixed) + sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t """ if self._degree_fixed == 1: diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 6b39d95752b..004078b4559 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -2089,7 +2089,7 @@ def _isomorphic_ring(self): base_image = self.base_ring().modulus().change_ring(isomorphic_ring).any_root() base_to_isomorphic_ring = self.base_ring().hom([isomorphic_ring(base_image)]) modulus = self.modulus().map_coefficients(base_to_isomorphic_ring) - gen = modulus.any_root(assume_squarefree=True, degree=-1) + gen = modulus.any_root(assume_squarefree=True, degree=1, assume_distinct_deg=True) homspace = Hom(self, isomorphic_ring) to_isomorphic_ring = homspace.__make_element_class__(SetMorphism)(homspace, diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index 58bed0a67a4..1f0fd8bd34e 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -271,7 +271,7 @@ def is_HCP(f, check_monic_irreducible=True): continue if d < h and d not in h2list: return zero - jp = fp.any_root(degree=-1, assume_squarefree=True) + jp = fp.any_root(degree=1, assume_squarefree=True, assume_distinct_deg=True) E = EllipticCurve(j=jp) if E.is_supersingular(): continue diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 81d4a8ea359..d87661b07b5 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -2180,7 +2180,7 @@ def point_of_order(E, n): sage: from sage.schemes.elliptic_curves.ell_field import point_of_order sage: E = EllipticCurve(GF(101), [1,2,3,4,5]) - sage: P = point_of_order(E, 5); P + sage: P = point_of_order(E, 5); P # random (50*Y^5 + 48*Y^4 + 26*Y^3 + 37*Y^2 + 48*Y + 15 : 25*Y^5 + 31*Y^4 + 79*Y^3 + 39*Y^2 + 3*Y + 20 : 1) sage: P.base_ring() Finite Field in Y of size 101^6 @@ -2191,7 +2191,7 @@ def point_of_order(E, n): :: - sage: Q = point_of_order(E, 8); Q + sage: Q = point_of_order(E, 8); Q # random (69*x^5 + 24*x^4 + 100*x^3 + 65*x^2 + 88*x + 97 : 65*x^5 + 28*x^4 + 5*x^3 + 45*x^2 + 42*x + 18 : 1) sage: 8*Q == 0 and 4*Q != 0 True From 5f50f16c588d66d4f82d7a2f507905575701eee3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 31 Jan 2024 19:06:44 -0800 Subject: [PATCH 069/191] configure.ac: Remove outdated comments --- configure.ac | 8 -------- 1 file changed, 8 deletions(-) diff --git a/configure.ac b/configure.ac index 6e124ed6328..dc75396cf2f 100644 --- a/configure.ac +++ b/configure.ac @@ -57,13 +57,6 @@ dnl avoid involving libtool by using it to get the shared library extension. AC_LIB_RPATH AC_SUBST(SHLIBEXT, "${acl_shlibext}") -#--------------------------------------------------------- -# -# This is essentially the configure part of the old "install" file. -# Over time, this should be changed to proper autoconf style with -# configure options. -# - ######################################################################## # Set various environment variables (needed at configure time) ######################################################################## @@ -567,7 +560,6 @@ AS_VAR_SET([sage_use_system_gcc], [force]) SAGE_SPKG_COLLECT() -dnl AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([build/make/Makefile-auto build/make/Makefile]) AC_CONFIG_FILES([src/bin/sage-env-config src/bin/sage-src-env-config build/bin/sage-build-env-config]) From 3694e1d2f4f10ebcb28d4fef94f9aff08d9dc018 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 10:19:22 -0800 Subject: [PATCH 070/191] configure.ac: Remove redundant check for latex (we check it properly in texlive/spkg-configure.m4) --- configure.ac | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/configure.ac b/configure.ac index dc75396cf2f..3d98fb1eeb6 100644 --- a/configure.ac +++ b/configure.ac @@ -321,17 +321,6 @@ else AC_MSG_ERROR([You do not have a suitable version of Python installed]) fi -# Check for Latex, the use of which is less important in Sage than -# it used to be, as it was at one time required to build any documentation -# but this is no longer so. -AC_CHECK_PROG(found_latex, latex, yes, no) -if test x$found_latex != xyes -then - AC_MSG_NOTICE([You do not have 'latex', which is recommended, but not]) - AC_MSG_NOTICE([required. Latex is only really used for building pdf]) - AC_MSG_NOTICE([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) -fi - # Check that perl is available, with version 5.8.0 or later. # Some packages need perl, however it is not clear whether Sage really # requires version >= 5.8.0. The R package *used* to require it, but From b2782cc19a81c1f2fa84f7ff6c791af1d08915de Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 10:29:43 -0800 Subject: [PATCH 071/191] configure.ac: Remove specific error messages for ancient unsupported platforms; do not invite users to port to random unsupported platforms --- configure.ac | 69 +++------------------------------------------------- 1 file changed, 3 insertions(+), 66 deletions(-) diff --git a/configure.ac b/configure.ac index 3d98fb1eeb6..2e7288a17ce 100644 --- a/configure.ac +++ b/configure.ac @@ -136,79 +136,16 @@ AC_CANONICAL_BUILD() AC_CANONICAL_HOST() case $host in -*-*-sunos*|*-*-solaris2.[1-9]) -AC_MSG_ERROR([[ -Sage is not supported on any version of Solaris earlier than 10. -Sage has been tested on the first release of Solaris 10 -(03/2005) and works on that. Sage may or may not work with -your version of Solaris. - -More information can be found about Sage on Solaris -on the Wiki at http://wiki.sagemath.org/solaris]]);; - -*-*-darwin[1-7].*) -AC_MSG_ERROR([[ -Sage has never been built on OS X 10.3 (Panther) -or earlier. The oldest version of OS X successfully used -is OS X version 10.4 (Tiger). You might consider updating -your version of OS X if your hardware permits this, but -Apple charges for upgrades of OS X]]);; - -*-*-hpux*) -AC_MSG_ERROR([[ -You are attempting to build Sage on HP's HP-UX operating system, -which is not a supported platform for Sage yet though -some work has been done on HP-UX. A port does not look to -be particularly difficult. Some information can be -found on the Sage Wiki at http://wiki.sagemath.org/HP-UX - -If you would like to help port Sage to HP-UX, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -*-*-aix*) -AC_MSG_ERROR([[ -You are attempting to build Sage on IBM's AIX operating system, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to AIX, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -*-*-irix*) -AC_MSG_ERROR([[ -You are attempting to build Sage on SGI's IRIX operating system, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to IRIX, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -*-*-osf*) -AC_MSG_ERROR([[ -You are attempting to build Sage on HP's Tru64 operating system, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to Tru64, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; - -# The following are all supported platforms. +dnl The following are all supported platforms. *-*-freebsd*);; *-*-linux*);; *-*-darwin*);; -*-*-solaris*);; -# Wildcard for other unsupported platforms +dnl Wildcard for unsupported platforms *) AC_MSG_ERROR([[ You are attempting to build Sage on $host, -which is not a supported platform for Sage yet. Things may or -may not work. If you would like to help port Sage to $host, -please join the sage-devel discussion list - see -http://groups.google.com/group/sage-devel -The Sage community would also appreciate any patches you submit]]);; +which is not a supported platform for Sage]]);; esac From 072b009c4d87b525deeca845cd606ad3135b681a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 10:31:20 -0800 Subject: [PATCH 072/191] configure.ac: Remove AIX-specific message --- configure.ac | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/configure.ac b/configure.ac index 2e7288a17ce..9e5e2b255ae 100644 --- a/configure.ac +++ b/configure.ac @@ -322,16 +322,6 @@ AC_CHECK_HEADER([complex.h],[],[ # First check for something that should be in any maths library (sqrt). AC_LANG(C++) AC_CHECK_LIB(m,sqrt,[],[ - AC_MSG_NOTICE([This system has no maths library installed.]) - # On AIX this is not installed by default - strange as that might seem. - # but is in a fileset bos.adt.libm. However, the fileset bos.adt - # includes other things that are probably useful. - if test "x`uname`" = 'xAIX' - then - AC_MSG_NOTICE([On AIX, libm is contained in the bos.adt.libm fileset.]) - AC_MSG_NOTICE([Actually, we recommend to install the complete bos.adt fileset.]) - AC_MSG_NOTICE([This needs to be performed by a system administrator.]) - fi AC_MSG_ERROR([Exiting, since a maths library was not found.]) ]) From 2e172272c0c2edc7343518966091546a0c343d4c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 10:34:14 -0800 Subject: [PATCH 073/191] configure.ac, m4/sage_check_osx_supported.m4: Remove tests and messages for prehistoric OS X versions --- configure.ac | 4 ---- m4/sage_check_osx_supported.m4 | 44 ---------------------------------- 2 files changed, 48 deletions(-) delete mode 100644 m4/sage_check_osx_supported.m4 diff --git a/configure.ac b/configure.ac index 9e5e2b255ae..b544257607f 100644 --- a/configure.ac +++ b/configure.ac @@ -325,15 +325,11 @@ AC_CHECK_LIB(m,sqrt,[],[ AC_MSG_ERROR([Exiting, since a maths library was not found.]) ]) -# Check for system services - # Check that we are not building in a directory containing spaces AS_IF([echo "$ac_pwd" |grep " " >/dev/null], AC_MSG_ERROR([the path to the Sage root directory ($ac_pwd) contains a space. Sage will not build correctly in this case]) ) -SAGE_CHECK_OSX_SUPPORTED() - ############################################################################### # Collect substitutions for build/make/Makefile.in ############################################################################### diff --git a/m4/sage_check_osx_supported.m4 b/m4/sage_check_osx_supported.m4 deleted file mode 100644 index 36984469c24..00000000000 --- a/m4/sage_check_osx_supported.m4 +++ /dev/null @@ -1,44 +0,0 @@ -AC_DEFUN([SAGE_CHECK_OSX_SUPPORTED], [ - AC_REQUIRE([AC_CANONICAL_HOST]) - AS_CASE([$host], - [*-apple-darwin*], [ - # Warning: xcodebuild does not seem to be maintained in Xcode 4.3 - # or later, so do not rely on the variable XCODE_VERS with OS X - # 10.7 or later. - changequote(<,>) - XCODE_VERS=`xcodebuild -version 2> /dev/null | grep Xcode | sed -e 's/[A-Za-z ]//g'` - changequote([,]) - if test -z $XCODE_VERS; then - XCODE_VERS="2" - fi - XCODE_VERS_MAJOR=`echo $XCODE_VERS | cut '-d.' -f1` - DARWIN_VERSION=`uname -r | cut '-d.' -f1` - if test $DARWIN_VERSION -gt 10; then - echo "You are using OS X Lion (or later)." - echo "You are strongly advised to install Apple's latest Xcode" - echo "unless you already have it. You can install this using" - echo "the App Store. Also, make sure you install Xcode's" - echo "Command Line Tools -- see Sage's README.txt." - elif test $XCODE_VERS_MAJOR -gt 2; then - echo "You are using Xcode version $XCODE_VERS." - echo "You are strongly advised to install Apple's latest Xcode" - echo "unless you already have it. You can download this from" - echo "http://developer.apple.com/downloads/." - echo "If using Xcode 4.3 or later, make sure you install Xcode's" - echo "Command Line Tools -- see Sage's README.txt." - else - echo "You are using Xcode version 1 or 2" - echo "WARNING: You are strongly advised to install the" - echo "latest version of Apple's Xcode for your platform," - echo "unless you already have it." - if test $DARWIN_VERSION -eq 10; then - echo "Probably you need Xcode 3.2.6" - elif test $DARWIN_VERSION -eq 9; then - echo "Probably you need Xcode 3.1.4" - elif test $DARWIN_VERSION -lt 9; then - echo "Probably you need Xcode 2.5" - fi - fi >& AS_MESSAGE_FD - - ]) -]) From 7235741fc5df053aabbaae5906a290054f10f7fe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 10:42:49 -0800 Subject: [PATCH 074/191] configure.ac: Update copyright, according to 'git blame -M -C configure.ac -w --date=format:%Y | sort -k2' --- configure.ac | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b544257607f..0384ea1cd85 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,14 @@ #***************************************************************************** # Copyright (C) 2005-2007 William Stein -# Copyright (C) 2009-2011 David Kirkby -# Copyright (C) 2012-2016 Jeroen Demeyer +# 2009-2011 David Kirkby +# 2012-2015 Volker Braun +# 2012-2019 Jeroen Demeyer +# 2014-2017 François Bissey +# 2016-2022 Matthias Koeppe +# 2017-2018 Erik M. Bray +# 2018 Dima Pasechnik +# 2020 Jonathan Kliem +# 2021-2023 Michael Orlitzky # # 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 From 5ec54b7ea6386b799bb9afe7aa2ec393d4b262f1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 13:30:42 -0800 Subject: [PATCH 075/191] build/pkgs/gmp/spkg-install.in: Remove code for outdated platforms --- build/pkgs/gmp/spkg-install.in | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/build/pkgs/gmp/spkg-install.in b/build/pkgs/gmp/spkg-install.in index 27ebbd88c6b..4cc11c8d0fa 100644 --- a/build/pkgs/gmp/spkg-install.in +++ b/build/pkgs/gmp/spkg-install.in @@ -28,8 +28,6 @@ fi case "$UNAME" in - SunOS) - true;; # Auto-detect ABI Darwin) # In some cases (see SAGE_ROOT/spkg/bin/sage-env), on Darwin, # CC might be set to clang, but GMP doesn't seem to build @@ -92,26 +90,8 @@ case "$UNAME" in rm -f foo foo.c fi ;; # Linux - *) # e.g. AIX or HP-UX - echo >&2 "Warning: Your platform ($UNAME) isn't yet explicitly supported" \ - "by this GMP spkg, i.e., by Sage's part of it." esac -# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs. -# See #12765, #12751, and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496 -if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ] ; then - gcc_version=`$CC -dumpversion` - case "$gcc_version" in - 4.7.0) - required_cflags="$required_cflags -O0 -finline-functions -fschedule-insns" - echo >&2 "Warning: Disabling almost all optimization due to a bug in GCC 4.7.0" - echo >&2 " on Itanium, which otherwise would break the build." - echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" - echo >&2 " for current status and further details." - ;; - esac -fi - export ABI CFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe(r). # We don't export CPPFLAGS here, since we don't (have to) modify them. From 49b7ebe7916d977840c6adbe9af10701b55ad217 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 13:31:48 -0800 Subject: [PATCH 076/191] build/bin/testcxx.sh: Unused, remove --- build/bin/testcxx.sh | 129 ------------------------------------------- 1 file changed, 129 deletions(-) delete mode 100755 build/bin/testcxx.sh diff --git a/build/bin/testcxx.sh b/build/bin/testcxx.sh deleted file mode 100755 index 0046bca13ac..00000000000 --- a/build/bin/testcxx.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/sh -# Determine the type of C++ compiler, which can later -# be used to determine the flags the compiler -# will want. This is done by testing what the -# C++ compiler's pre-processor defines. For example, -# g++ will always define __GNUC__ (as does gcc). -# -# This will typically be called as (don't quote $CXX): -# testcxx.sh $CXX - -# Copyright Dr. David Kirkby -# Released under the GPL version 2, or any later version -# the user wishes to use. -# Considerably cleaned up by Peter Jeremy. -# Further clean-up by Jeroen Demeyer (#12821). -# Helpful comments by William Stein. - -# Some of the compilers have been tested, though some have -# not, due to a lack of hardware or software. - -# Documentation on the compilers is taken from many source -# in particular, for the commercial Unix compilers: - -# HP-UX C and C++ compiler. -# http://docs.hp.com/en/7730/newhelp0610/preprocess.htm - -# IBM Compiler Reference - XL C/C++ for AIX, V10.1 -# http://www-01.ibm.com/support/docview.wss?uid=swg27012860&aid=1 - -# Using HP C++ for Tru64 UNIX and Linux Alpha -# http://h30097.www3.hp.com/cplus/ugu_impl.html#implem_chap - - -# Define a function to display usage information. -usage() -{ -cat <&2 -Usage: $0 name_or_path_to_the_C++_compiler [optional arguments] - -Typically, this will be called as -$0 \$CXX - - The script will print one of the following to indicate the C++ compiler - - GCC - For g++ or a g++ like C++ compiler on any platform - Sun_Studio - For Sun Studio or earlier Sun C++ compiler - HP_on_Tru64 - For a C++ compiler produced by HP/Compaq for Tru64 - HP_on_HP-UX - For a C++ compiler produced by HP/Compaq for HP-UX - IBM_on_AIX - For a C++ compiler produced by IBM for AIX - HP_on_Alpha_Linux - For a C++ compiler produced by HP for Alpha linux - (This script has not been tested on Alpha Linux, - but is based on HP's documentation) - Unknown - If the C++ compiler type is unknown - -EOF -} - -# Exit if the user does not supply any command line arguments. -if [ $# -lt 1 ] ; then - usage - exit 2 -fi - -# Create a test file. It does not need to be a complete -# C++ file, as it is only pre-processed. So there is no -# need for a 'main' - -if [ -z "$SAGE_ROOT" ]; then - echo "The SAGE_ROOT environment variable must be set to" - echo "the root of the Sage installation" - exit 1 -fi - -mkdir -p "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to create the build directory." - exit 1 -fi - -cd "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to change into the build directory." - exit 1 -fi -TESTFILE=sage-testcxx-$$.cpp - -cat >$TESTFILE <<"E*O*F" -#ifdef __GNUC__ -GCC -#define KNOWN_CXX -#endif - -#ifdef __SUNPRO_CC -Sun_Studio -#define KNOWN_CXX -#endif - -#ifdef __digital__ -#ifdef __DECCXX -HP_on_Tru64 -#define KNOWN_CXX -#endif -#endif - -#ifdef __linux__ -#ifdef __DECCXX -HP_on_Alpha_Linux -#define KNOWN_CXX -#endif -#endif - -#ifdef __HP_aCC -HP_on_HP-UX -#define KNOWN_CXX -#endif - -#ifdef __xlC__ -IBM_on_AIX -#define KNOWN_CXX -#endif - -#ifndef KNOWN_CXX -Unknown -#endif -E*O*F - -"$@" -E $TESTFILE | sed -n '/^[A-Z]/{s/ //g;p;}' -rm -f $TESTFILE -exit 0 From 65e60ee7421d1a2a99221bb03589ccb54ccb4e55 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 13:38:06 -0800 Subject: [PATCH 077/191] build/pkgs/m4ri*/spkg-install.in: Remove use of testcc.sh, testcflags.sh --- build/pkgs/m4ri/spkg-install.in | 11 +---------- build/pkgs/m4rie/spkg-install.in | 9 +-------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/build/pkgs/m4ri/spkg-install.in b/build/pkgs/m4ri/spkg-install.in index 0af3234ed16..392265fc116 100644 --- a/build/pkgs/m4ri/spkg-install.in +++ b/build/pkgs/m4ri/spkg-install.in @@ -1,14 +1,5 @@ CFLAGS="$CFLAGS -I$SAGE_LOCAL/include -g" - -COMPILER=`testcc.sh $CC` - -if [ "$COMPILER" = "GCC" ] ; then - CFLAGS="$CFLAGS -fPIC -Wall -pedantic" -elif [ "$COMPILER" = "Sun_Studio" ] ; then - CFLAGS="$CFLAGS -Kpic" -elif [ "$COMPILER" = "HP_on_HP-UX" ] ; then - CFLAGS="$CFLAGS + z" -fi +CFLAGS="$CFLAGS -fPIC -Wall -pedantic" if [ "x$SAGE_DEBUG" = "xyes" ]; then ENABLE_DEBUG="--enable-debug" diff --git a/build/pkgs/m4rie/spkg-install.in b/build/pkgs/m4rie/spkg-install.in index bc0c13ffe1e..4d242bc52cf 100644 --- a/build/pkgs/m4rie/spkg-install.in +++ b/build/pkgs/m4rie/spkg-install.in @@ -5,16 +5,9 @@ ROOT_DIR="`pwd`" INCLUDES="-I$SAGE_LOCAL/include" LIBDIRS="-L$SAGE_LOCAL/lib" -CFLAGS="`testcflags.sh -fPIC -Wall -pedantic -g` $CFLAGS $INCLUDES" +CFLAGS="-fPIC -Wall -pedantic -g $CFLAGS $INCLUDES" LDFLAGS="$LIBDIRS $LDFLAGS" -COMPILER=`testcc.sh $CC` -if [ "$COMPILER" = "Sun_Studio" ] ; then - CFLAGS="$CFLAGS -Kpic" -elif [ "$COMPILER" = "HP_on_HP-UX" ] ; then - CFLAGS="$CFLAGS + z" -fi - CPPFLAGS="$INCLUDES" if [ "x$SAGE_DEBUG" = "xyes" ]; then From 5086ba987e230018596d92e6eea5a60bf1780ae1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 13:40:11 -0800 Subject: [PATCH 078/191] build/pkgs/python3/spkg-build.in: Remove code for SunOS --- build/pkgs/python3/spkg-build.in | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in index 4cd421bf4b8..00d073802d4 100644 --- a/build/pkgs/python3/spkg-build.in +++ b/build/pkgs/python3/spkg-build.in @@ -48,12 +48,6 @@ if [ "$UNAME" = Darwin ]; then echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." CC=clang fi -elif [ "$UNAME" = SunOS ]; then - # Enable some C99 features on Solaris. This in particular enables - # the isinf() and isfinite() functions. It works both for C and - # C++ code (which is not true for -std=c99). See - # https://github.com/sagemath/sage/issues/14265 - export CFLAGS="-D__C99FEATURES__ $CFLAGS" fi # Use EXTRA_CFLAGS for user-defined CFLAGS since Python puts its own From 5384ddc6063c6067595d8784b3729e853eb70bae Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 13:43:18 -0800 Subject: [PATCH 079/191] build/bin/testcc.sh: Unused, remove --- build/bin/testcc.sh | 136 -------------------------------------------- 1 file changed, 136 deletions(-) delete mode 100755 build/bin/testcc.sh diff --git a/build/bin/testcc.sh b/build/bin/testcc.sh deleted file mode 100755 index 05c8bf08fc3..00000000000 --- a/build/bin/testcc.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh -# Determine the type of C compiler, which can later -# be used to determine the flags the C compiler -# will want. This is done by testing what the -# C compiler's pre-processor defines. For example, -# gcc will always define __GNUC__ -# -# This will typically be called as (don't quote $CC): -# testcc.sh $CC - -# Copyright Dr. David Kirkby -# Released under the GPL version 2, or any later version -# the user wishes to use. -# Considerably cleaned up by Peter Jeremy. -# Further clean-up by Jeroen Demeyer (#12821). -# Helpful comments by William Stein. - -# Some of the compilers have been tested, though some have -# not, due to a lack of hardware or software. - -# Documentation on the compilers is taken from many source -# in particular, for the commercial Unix compilers: - -# HP-UX C and C++ compiler. -# http://docs.hp.com/en/7730/newhelp0610/preprocess.htm - -# IBM Compiler Reference - XL C/C++ for AIX, V10.1 -# http://www-01.ibm.com/support/docview.wss?uid=swg27012860&aid=1 - -# Using HP C++ for Tru64 UNIX and Linux Alpha -# http://h30097.www3.hp.com/cplus/ugu_impl.html#implem_chap - - -# Define a function to display usage information. -usage() -{ -cat <&2 -Usage: $0 name_or_path_to_the_C_compiler [optional arguments] - -Typically, this will be called as -$0 \$CC - - The script will print one of the following to indicate the C compiler - - GCC - For gcc or a gcc-like C compiler on any platform - Sun_Studio - For Sun Studio or earlier Sun C compiler - HP_on_Tru64 - For a C compiler produced by HP/Compaq for Tru64 - HP_on_HP-UX - For a C compiler produced by HP/Compaq for HP-UX - IBM_on_AIX - For a C compiler produced by IBM for AIX - HP_on_Alpha_Linux - For a C compiler produced by HP for Alpha linux - (This script has not been tested on Alpha Linux, - but is based on HP's documentation) - Unknown - If the C compiler type is unknown - -EOF -} - -# Exit if the user does not supply any command line arguments. -if [ $# -lt 1 ] ; then - usage - exit 2 -fi - -# Create a test file. It does not need to be a complete -# C file, as it is only pre-processed. So there is no -# need for a 'main' - -if [ -z "$SAGE_ROOT" ]; then - echo "The SAGE_ROOT environment variable must be set to" - echo "the root of the Sage installation" - exit 1 -fi - -mkdir -p "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to create the build directory." - exit 1 -fi - -cd "$SAGE_LOCAL/var/tmp/sage/build" -if [ $? -ne 0 ]; then - echo "Error while trying to change into the build directory." - exit 1 -fi -TESTFILE=sage-testcc-$$.c - -cat >$TESTFILE <<"E*O*F" -#ifdef __GNUC__ -GCC -#define KNOWN_CC -#endif - -#ifdef __SUNPRO_C -Sun_Studio -#define KNOWN_CC -#endif - -/* - * I've not found any official documentation to suggest __DECC - * would be defined, but __DECCC is defined for C++, - * and a Google on __DECC shows many hits. I do not have - * easy access to a Tru64 system. - */ -#ifdef __digital__ -#ifdef __DECC -HP_on_Tru64 -#define KNOWN_CC -#endif -#endif - -/* Untested, as I have no access to a Alpha Linux system. */ -#ifdef __linux__ -#ifdef __DECCC -HP_on_Alpha_Linux -#define KNOWN_CC -#endif -#endif - -#ifdef __HP_cc -HP_on_HP-UX -#define KNOWN_CC -#endif - -#ifdef __xlC__ -IBM_on_AIX -#define KNOWN_CC -#endif - -#ifndef KNOWN_CC -Unknown -#endif -E*O*F - -"$@" -E $TESTFILE | sed -n '/^[A-Z]/{s/ //g;p;}' -rm -f $TESTFILE -exit 0 From ada1aac8ee3526c32876c32467fd2b77bd463d17 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 14:28:35 -0800 Subject: [PATCH 080/191] build/pkgs/gmp/spkg-install.in: Remove outdated OS X / clang support code --- build/pkgs/gmp/spkg-install.in | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/build/pkgs/gmp/spkg-install.in b/build/pkgs/gmp/spkg-install.in index 4cc11c8d0fa..baacc5132b1 100644 --- a/build/pkgs/gmp/spkg-install.in +++ b/build/pkgs/gmp/spkg-install.in @@ -28,35 +28,6 @@ fi case "$UNAME" in - Darwin) - # In some cases (see SAGE_ROOT/spkg/bin/sage-env), on Darwin, - # CC might be set to clang, but GMP doesn't seem to build - # with clang. - CLANG=`command -v clang` - GCC=`command -v gcc` - if [ -n "$CC" ] && [ "$CC" = "$CLANG" ] && [ -n "$GCC" ] ; then - export CC="$GCC" - fi - # Do not set ABI=32 on MacOS X 10.6 (Darwin 10) and later, since - # there everything defaults to 64-bit: - if [ "`uname -r | sed 's/\..*//'`" -lt 10 ]; then - # Assume MacOS X 10.4 or 10.5 (Darwin 8 or 9); also, PPC CPUs - # are only supported by these, not later versions. - echo "Building a 32-bit version of GMP, which is the only supported option." - ABI=32 - case "`uname -m`" in - ppc|ppc64|[Pp]ower*) # Apple's 'uname' returns strange strings - # The Darwin assembler rejects code using an - # extended instruction set by default (cf. #8664): - required_cflags="$required_cflags -Wa,-force_cpusubtype_ALL" - ;; - esac - else - # Darwin 10 (MacOS X 10.6) or later. - # We don't have to set ABI here. - echo "Building a 64-bit version of GMP, which is the default." - fi - ;; # Darwin Linux) # GMP fails to build on 32-bit operating systems running on # 64-bit CPUs if CFLAGS happen to contain '-m32' and ABI is From f9d8e7b42f0c0e6f3e2207055982e02f4e9aa68c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 14:36:08 -0800 Subject: [PATCH 081/191] build/pkgs/{lie,rubiks}/spkg-install.in: Remove SunOS support code --- build/pkgs/lie/spkg-install.in | 3 --- build/pkgs/rubiks/spkg-install.in | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/build/pkgs/lie/spkg-install.in b/build/pkgs/lie/spkg-install.in index 04f6b9372e6..bfaf9d5f17c 100644 --- a/build/pkgs/lie/spkg-install.in +++ b/build/pkgs/lie/spkg-install.in @@ -7,9 +7,6 @@ die () { cd src chmod -R a+rX . -if [ $UNAME = "SunOS" ]; then - sage-apply-patches -d solaris || die "Error patching LiE." -fi # Building LiE in parallel is broken export MAKE="$MAKE -j1" diff --git a/build/pkgs/rubiks/spkg-install.in b/build/pkgs/rubiks/spkg-install.in index 893485ea0bd..5c882a0bfd5 100644 --- a/build/pkgs/rubiks/spkg-install.in +++ b/build/pkgs/rubiks/spkg-install.in @@ -36,11 +36,7 @@ echo " " # End of pretty general spkg-install file. # Now do the specific things needed for this package (rubiks) -if [ $UNAME = "SunOS" ]; then - INSTALL=cp; export INSTALL -else - INSTALL=install; export INSTALL -fi +INSTALL=install; export INSTALL if [ $UNAME = "Darwin" ]; then # #34293: Work around compiler hang From 1af9469e2651549a417fc78bdfa92f65eeb9be34 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 18 Dec 2023 14:45:45 -0800 Subject: [PATCH 082/191] build/pkgs/{openblas,openssl,r}/spkg-configure.m4: Remove Cygwin support code --- build/pkgs/gfortran/spkg-configure.m4 | 6 ------ build/pkgs/openblas/spkg-configure.m4 | 8 +------- build/pkgs/openssl/spkg-configure.m4 | 6 ------ build/pkgs/r/spkg-configure.m4 | 10 ++-------- 4 files changed, 3 insertions(+), 27 deletions(-) diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index d2841f740aa..0efcffc025e 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -93,10 +93,4 @@ SAGE_SPKG_CONFIGURE([gfortran], [ ]) ]) fi - AS_CASE([$host], - [*-*-cygwin*], [AS_VAR_IF([sage_spkg_install_gfortran], [yes], [ - AS_VAR_APPEND([SAGE_SPKG_ERRORS], [" - On Cygwin, gfortran must be installed as a system package. This is an error."]) - ]) - ]) ]) diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index 06affdacff8..2d93bc7a444 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -66,12 +66,7 @@ SAGE_SPKG_CONFIGURE([openblas], [ ]) ], [ dnl No openblas.pc - AS_CASE([$host], - [*-*-cygwin*], [dnl #29538 - workaround failing build of matplotlib etc. - AS_VAR_SET([HAVE_OPENBLAS], [no]) - AC_MSG_RESULT([$HAVE_OPENBLAS, test for OpenBLAS disabled on Cygwin]) - ], - [dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as + dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as dnl part of openblas_get_config. We reject all older versions. AC_SEARCH_LIBS([openblas_get_config], [openblas cblas blas], [ AS_IF([test x"$ac_cv_search_openblas_get_config" != x"none required"], [ @@ -109,7 +104,6 @@ SAGE_SPKG_CONFIGURE([openblas], [ AC_LANG_POP([C]) AC_MSG_RESULT([$HAVE_OPENBLAS]) ]) - ]) AC_SEARCH_LIBS([cblas_dgemm], [openblas cblas blas], [ AS_VAR_SET([HAVE_CBLAS_DGEMM], [yes]) AS_IF([test x"$ac_cv_search_cblas_dgemm" != x"none required"], [ diff --git a/build/pkgs/openssl/spkg-configure.m4 b/build/pkgs/openssl/spkg-configure.m4 index 51dcb7de779..70270ea9296 100644 --- a/build/pkgs/openssl/spkg-configure.m4 +++ b/build/pkgs/openssl/spkg-configure.m4 @@ -48,12 +48,6 @@ SAGE_SPKG_CONFIGURE([openssl], [ ], [dnl No openssl found sage_spkg_install_openssl=yes ]) - AS_CASE([$host], - [*-*-cygwin*], [AS_VAR_IF([sage_spkg_install_openssl], [yes], [ - AS_VAR_APPEND([SAGE_SPKG_ERRORS], [" -On Cygwin, openssl must be installed as a system package. This is an error."]) - ]) - ]) ], [dnl REQUIRED-CHECK AC_REQUIRE([SAGE_SPKG_CONFIGURE_PYTHON3]) AC_REQUIRE([SAGE_SPKG_CONFIGURE_CURL]) diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 index b31f6bef2ec..7db0396d355 100644 --- a/build/pkgs/r/spkg-configure.m4 +++ b/build/pkgs/r/spkg-configure.m4 @@ -1,12 +1,7 @@ SAGE_SPKG_CONFIGURE([r], [ dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements m4_pushdef([SAGE_R_MINVER],["3.5"]) - AS_CASE([$host], - [*-*-cygwin*], [ - dnl #29486: rpy2 2.8.x does not build against system R on cygwin. - sage_spkg_install_r=yes - ], [ - PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [ + PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [ AC_PATH_PROG([R], [R]) AS_IF([test "x$R" = x], [ AC_MSG_NOTICE([R is not found]) @@ -15,7 +10,6 @@ SAGE_SPKG_CONFIGURE([r], [ dnl TODO: check that versions of R and libR match sage_spkg_install_r=no ]) - ], [sage_spkg_install_r=yes]) - ]) + ], [sage_spkg_install_r=yes]) m4_popdef([SAGE_R_MINVER]) ]) From b2268fba2fba49c26fdc4e7b4e1f4727fb16d2b3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 7 Jan 2024 10:16:07 -0800 Subject: [PATCH 083/191] build/pkgs/openblas/spkg-configure.m4: Reindent, suppress some whitespace --- build/pkgs/openblas/spkg-configure.m4 | 234 +++++++++++++------------- 1 file changed, 114 insertions(+), 120 deletions(-) diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index 2d93bc7a444..94175da210b 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -1,137 +1,131 @@ -SAGE_SPKG_CONFIGURE([openblas], [ - dnl CHECK - SAGE_SPKG_DEPCHECK([gfortran], [ - SAVE_LIBS="$LIBS" - SAVE_CFLAGS="$CFLAGS" - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MAJOR], [0]) - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MINOR], [2]) - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MICRO], [20]) - m4_pushdef([SAGE_OPENBLAS_MIN_VERSION], [SAGE_OPENBLAS_MIN_VERSION_MAJOR.SAGE_OPENBLAS_MIN_VERSION_MINOR.SAGE_OPENBLAS_MIN_VERSION_MICRO]) - dnl Reject openblas 0.3.22 - https://github.com/sagemath/sage/pull/35371 - m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MAJOR], [0]) - m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MINOR], [3]) - m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MICRO], [99]) - m4_pushdef([SAGE_OPENBLAS_LT_VERSION], [SAGE_OPENBLAS_LT_VERSION_MAJOR.SAGE_OPENBLAS_LT_VERSION_MINOR.SAGE_OPENBLAS_LT_VERSION_MICRO]) - PKG_CHECK_MODULES([OPENBLAS], [openblas >= ]SAGE_OPENBLAS_MIN_VERSION [openblas < ]SAGE_OPENBLAS_LT_VERSION, [ - LIBS="$OPENBLAS_LIBS $LIBS" - CFLAGS="$OPENBLAS_CFLAGS $CFLAGS" - PKG_CHECK_VAR([OPENBLASPCDIR], [openblas], [pcfiledir], [ - sage_install_blas_pc=yes - AC_CHECK_FUNC([cblas_dgemm], [dnl openblas works as cblas - sage_install_cblas_pc=yes - ], [ - dnl openblas does not work as cblas; try to use system cblas as is - PKG_CHECK_MODULES([CBLAS], [cblas], [], [sage_spkg_install_openblas=yes]) - ]) - dnl Check all name manglings that AC_FC_FUNC could check based on the - dnl characteristics of the Fortran compiler - m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [ - AC_CHECK_FUNC(dgeqrf_mangled, [ - AS_VAR_SET([HAVE_DGEQRF], [yes]) - ]) - ]) - AS_IF([test x$HAVE_DGEQRF = xyes], [dnl openblas works as lapack - sage_install_lapack_pc=yes - ], [ - dnl openblas does not work as lapack; try to use system lapack as is - PKG_CHECK_MODULES([LAPACK], [lapack], [], [sage_spkg_install_openblas=yes]) +SAGE_SPKG_CONFIGURE([openblas], [dnl CHECK + SAGE_SPKG_DEPCHECK([gfortran], [dnl + SAVE_LIBS="$LIBS" + SAVE_CFLAGS="$CFLAGS" + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MAJOR], [0]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MINOR], [2]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MICRO], [20]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION], [SAGE_OPENBLAS_MIN_VERSION_MAJOR.SAGE_OPENBLAS_MIN_VERSION_MINOR.SAGE_OPENBLAS_MIN_VERSION_MICRO]) + dnl Reject openblas 0.3.22 - https://github.com/sagemath/sage/pull/35371 + m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MAJOR], [0]) + m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MINOR], [3]) + m4_pushdef([SAGE_OPENBLAS_LT_VERSION_MICRO], [99]) + m4_pushdef([SAGE_OPENBLAS_LT_VERSION], [SAGE_OPENBLAS_LT_VERSION_MAJOR.SAGE_OPENBLAS_LT_VERSION_MINOR.SAGE_OPENBLAS_LT_VERSION_MICRO]) + PKG_CHECK_MODULES([OPENBLAS], [openblas >= ]SAGE_OPENBLAS_MIN_VERSION [openblas < ]SAGE_OPENBLAS_LT_VERSION, [dnl Have openblas.pc + LIBS="$OPENBLAS_LIBS $LIBS" + CFLAGS="$OPENBLAS_CFLAGS $CFLAGS" + PKG_CHECK_VAR([OPENBLASPCDIR], [openblas], [pcfiledir], [dnl + sage_install_blas_pc=yes + AC_CHECK_FUNC([cblas_dgemm], [dnl openblas works as cblas + sage_install_cblas_pc=yes + ], [dnl openblas does not work as cblas; try to use system cblas as is + PKG_CHECK_MODULES([CBLAS], [cblas], [], [sage_spkg_install_openblas=yes]) + ]) + dnl Check all name manglings that AC_FC_FUNC could check based on the + dnl characteristics of the Fortran compiler + m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [dnl + AC_CHECK_FUNC(dgeqrf_mangled, [dnl + AS_VAR_SET([HAVE_DGEQRF], [yes]) ]) - ], [ - AC_MSG_WARN([Unable to locate the directory of openblas.pc. This should not happen!]) - sage_spkg_install_openblas=yes - ]) - AS_IF([test x$sage_spkg_install_openblas != xyes], [ - AC_MSG_CHECKING([the OpenBLAS version using openblas_get_config]) - AC_LANG_PUSH([C]) - AC_RUN_IFELSE([ - dnl Reject 0.3.22 - see https://github.com/sagemath/sage/pull/35377 - AC_LANG_PROGRAM([[#include - char *openblas_get_config(void); ]], - [[if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) - ], [ - AC_MSG_RESULT([good]) - ], [ - AC_MSG_RESULT([known bad version]) - sage_spkg_install_openblas=yes]) - AC_LANG_POP([C]) - ]) - AS_IF([test x$sage_spkg_install_openblas != xyes], [ + ]) + AS_IF([test x$HAVE_DGEQRF = xyes], [dnl openblas works as lapack + sage_install_lapack_pc=yes + ], [dnl openblas does not work as lapack; try to use system lapack as is + PKG_CHECK_MODULES([LAPACK], [lapack], [], [sage_spkg_install_openblas=yes]) + ]) + ], [dnl + AC_MSG_WARN([Unable to locate the directory of openblas.pc. This should not happen!]) + sage_spkg_install_openblas=yes + ]) + AS_IF([test x$sage_spkg_install_openblas != xyes], [dnl + AC_MSG_CHECKING([the OpenBLAS version using openblas_get_config]) + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([dnl Reject 0.3.22 - see https://github.com/sagemath/sage/pull/35377 + AC_LANG_PROGRAM([[#include + char *openblas_get_config(void); ]], + [[if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) + ], [dnl + AC_MSG_RESULT([good]) + ], [dnl + AC_MSG_RESULT([known bad version]) + sage_spkg_install_openblas=yes]) + AC_LANG_POP([C]) + ]) + AS_IF([test x$sage_spkg_install_openblas != xyes], [dnl AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) AC_SUBST([SAGE_OPENBLAS_PC_COMMAND], ["\$(LN) -sf \"$OPENBLASPCDIR/openblas.pc\" \"\$(@)\""]) - m4_foreach([blaslibnam], [blas, cblas, lapack], [ - AS_IF([test x$sage_install_]blaslibnam[_pc = xyes], [ - AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) - ]) + m4_foreach([blaslibnam], [blas, cblas, lapack], [dnl + AS_IF([test x$sage_install_]blaslibnam[_pc = xyes], [dnl + AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) + ]) + ]) + ]) + ], [dnl No openblas.pc + dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as + dnl part of openblas_get_config. We reject all older versions. + AC_SEARCH_LIBS([openblas_get_config], [openblas cblas blas], [dnl + AS_IF([test x"$ac_cv_search_openblas_get_config" != x"none required"], [dnl + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_openblas_get_config "]) ]) - ]) - ], [ - dnl No openblas.pc - dnl Recent OpenBLAS (>= 0.3.4, Dec 2018) provides the version number as - dnl part of openblas_get_config. We reject all older versions. - AC_SEARCH_LIBS([openblas_get_config], [openblas cblas blas], [ - AS_IF([test x"$ac_cv_search_openblas_get_config" != x"none required"], [ - AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_openblas_get_config "]) - ]) - AC_MSG_CHECKING([whether openblas_get_config indicates version >= ]SAGE_OPENBLAS_MIN_VERSION) - AC_LANG_PUSH([C]) - AC_RUN_IFELSE([ - AC_LANG_PROGRAM([[#include - #include - char *openblas_get_config(void); - int version[3]; ]], - [[version[0] = version[1] = version[2] = 0; - /*printf("%s", openblas_get_config());*/ - if (sscanf(openblas_get_config(), "OpenBLAS %d.%d.%d", - version, version+1, version+2) < 1) - return 1; - if ( 10000 * version[0] - + 100 * version[1] - + version[2] - < 10000 * ]]SAGE_OPENBLAS_MIN_VERSION_MAJOR[[ - + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ - + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) - return 1; - if ( 10000 * version[0] - + 100 * version[1] - + version[2] - >=10000 * ]]SAGE_OPENBLAS_LT_VERSION_MAJOR[[ - + 100 * ]]SAGE_OPENBLAS_LT_VERSION_MINOR[[ - + ]]SAGE_OPENBLAS_LT_VERSION_MICRO[[) - return 1; - if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) - ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])], - [AS_VAR_SET([HAVE_OPENBLAS], [yes])]) - AC_LANG_POP([C]) - AC_MSG_RESULT([$HAVE_OPENBLAS]) - ]) - AC_SEARCH_LIBS([cblas_dgemm], [openblas cblas blas], [ + AC_MSG_CHECKING([whether openblas_get_config indicates version >= ]SAGE_OPENBLAS_MIN_VERSION) + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([dnl + AC_LANG_PROGRAM([[#include + #include + char *openblas_get_config(void); + int version[3]; ]], + [[version[0] = version[1] = version[2] = 0; + /*printf("%s", openblas_get_config());*/ + if (sscanf(openblas_get_config(), "OpenBLAS %d.%d.%d", + version, version+1, version+2) < 1) + return 1; + if ( 10000 * version[0] + + 100 * version[1] + + version[2] + < 10000 * ]]SAGE_OPENBLAS_MIN_VERSION_MAJOR[[ + + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ + + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) + return 1; + if ( 10000 * version[0] + + 100 * version[1] + + version[2] + >=10000 * ]]SAGE_OPENBLAS_LT_VERSION_MAJOR[[ + + 100 * ]]SAGE_OPENBLAS_LT_VERSION_MINOR[[ + + ]]SAGE_OPENBLAS_LT_VERSION_MICRO[[) + return 1; + if (!strncmp(openblas_get_config(), "OpenBLAS 0.3.22", 15)) return 1;]]) + ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], + [AS_VAR_SET([HAVE_OPENBLAS], [no])], + [AS_VAR_SET([HAVE_OPENBLAS], [yes])]) + AC_LANG_POP([C]) + AC_MSG_RESULT([$HAVE_OPENBLAS]) + ]) + AC_SEARCH_LIBS([cblas_dgemm], [openblas cblas blas], [dnl AS_VAR_SET([HAVE_CBLAS_DGEMM], [yes]) - AS_IF([test x"$ac_cv_search_cblas_dgemm" != x"none required"], [ + AS_IF([test x"$ac_cv_search_cblas_dgemm" != x"none required"], [dnl AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_cblas_dgemm "]) ]) ], [], [-lgfortran]) - m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [ - AC_SEARCH_LIBS(dgeqrf_mangled, [openblas lapack], [ - AS_VAR_SET([HAVE_DGEQRF], [yes]) - AS_IF([test x"$ac_cv_search_]dgeqrf_mangled[" != x"none required"], [ - AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_]dgeqrf_mangled[ "]) - ]) - ], [], [-lgfortran]) + m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [dnl + AC_SEARCH_LIBS(dgeqrf_mangled, [openblas lapack], [dnl + AS_VAR_SET([HAVE_DGEQRF], [yes]) + AS_IF([test x"$ac_cv_search_]dgeqrf_mangled[" != x"none required"], [dnl + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_]dgeqrf_mangled[ "]) + ]) + ], [], [-lgfortran]) ]) - AS_IF([test x"$HAVE_OPENBLAS" = xyes -a x"$HAVE_CBLAS_DGEMM" = xyes -a x"$HAVE_DGEQRF" = xyes], [ + AS_IF([test x"$HAVE_OPENBLAS" = xyes -a x"$HAVE_CBLAS_DGEMM" = xyes -a x"$HAVE_DGEQRF" = xyes], [dnl AC_SUBST([OPENBLAS_LIBS]) AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) AC_SUBST([SAGE_OPENBLAS_PC_COMMAND], [" (echo \"Name: openblas\"; echo \"Description: OpenBLAS\"; echo \"Version: 0.3\"; echo \"Libs: $OPENBLAS_LIBS\") > \"\$(@)\""]) - m4_foreach([blaslibnam], [openblas, blas, cblas, lapack], [ + m4_foreach([blaslibnam], [openblas, blas, cblas, lapack], [dnl AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) ]) - ], [ - dnl No system BLAS found + ], [dnl No system BLAS found sage_spkg_install_openblas=yes ]) ]) - LIBS="$SAVE_LIBS" - CFLAGS="$SAVE_CFLAGS" - ]) - ] -) + LIBS="$SAVE_LIBS" + CFLAGS="$SAVE_CFLAGS" + ]) +]) From e0548a02609abecee6c0bde039fe577ec3c205b6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 7 Jan 2024 10:19:23 -0800 Subject: [PATCH 084/191] build/pkgs/r/spkg-configure.m4: Reindent, suppress some whitespace --- build/pkgs/r/spkg-configure.m4 | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/build/pkgs/r/spkg-configure.m4 b/build/pkgs/r/spkg-configure.m4 index 7db0396d355..0552d0c56ce 100644 --- a/build/pkgs/r/spkg-configure.m4 +++ b/build/pkgs/r/spkg-configure.m4 @@ -1,15 +1,14 @@ -SAGE_SPKG_CONFIGURE([r], [ - dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements - m4_pushdef([SAGE_R_MINVER],["3.5"]) - PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [ - AC_PATH_PROG([R], [R]) - AS_IF([test "x$R" = x], [ - AC_MSG_NOTICE([R is not found]) - sage_spkg_install_r=yes - ], [ - dnl TODO: check that versions of R and libR match - sage_spkg_install_r=no - ]) - ], [sage_spkg_install_r=yes]) - m4_popdef([SAGE_R_MINVER]) +SAGE_SPKG_CONFIGURE([r], [dnl + dnl https://rpy2.github.io/doc/v3.4.x/html/overview.html#requirements + m4_pushdef([SAGE_R_MINVER], ["3.5"]) + PKG_CHECK_MODULES([R], [libR >= SAGE_R_MINVER], [dnl + AC_PATH_PROG([R], [R]) + AS_IF([test "x$R" = x], [dnl + AC_MSG_NOTICE([R is not found]) + sage_spkg_install_r=yes + ], [dnl TODO: check that versions of R and libR match + sage_spkg_install_r=no + ]) + ], [sage_spkg_install_r=yes]) + m4_popdef([SAGE_R_MINVER]) ]) From b47a604ae08682481d62befd7cd0f6e1c1215da9 Mon Sep 17 00:00:00 2001 From: syndrakon Date: Thu, 1 Feb 2024 10:09:37 +0100 Subject: [PATCH 085/191] Fix style in comments and tests --- src/sage/algebras/quatalg/quaternion_algebra.py | 4 ++-- src/sage/modular/quatalg/brandt.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 50b7a2448ce..34a6dfda820 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -2394,7 +2394,8 @@ def _richcmp_(self, right, op): sage: I != I # indirect doctest False - TESTS: + TESTS:: + sage: B = QuaternionAlgebra(QQ,-1,-11) sage: i,j,k = B.gens() sage: I = B.ideal([1,i,j,i*j]) @@ -2419,7 +2420,6 @@ def _richcmp_(self, right, op): True sage: O >= O True - """ return self.free_module().__richcmp__(right.free_module(), op) diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 277d5c0d45c..bfe7f71f8df 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -1365,7 +1365,9 @@ def right_ideals(self, B=None): ideals_theta[J_theta] = [J] verbose("found %s of %s ideals" % (len(ideals), self.dimension()), level=2) if len(ideals) >= self.dimension(): - #order by basis matrix (as ideals were previously ordered) for backward compatibility and deterministic order of the output + # order by basis matrix (as ideals were previously + # ordered) for backward compatibility and + # deterministic order of the output ideals = tuple(sorted(ideals, key=lambda x: x.basis_matrix())) self.__right_ideals = ideals return ideals From a553cc0ed8c0428360958bf6a49bcb3d85ddbf57 Mon Sep 17 00:00:00 2001 From: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:25:27 +0000 Subject: [PATCH 086/191] Remove unnecessary bullet point --- src/doc/en/installation/index.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/en/installation/index.rst b/src/doc/en/installation/index.rst index be680fceea8..21d5649f2e0 100644 --- a/src/doc/en/installation/index.rst +++ b/src/doc/en/installation/index.rst @@ -71,18 +71,18 @@ Windows - **No development:** - - Enable Windows Subsystem for Linux (WSL) by following the - `official WSL setup guide - `_. Be - sure to do the steps to install WSL2 and set it as default. - Make sure to allocate enough RAM to WSL: 5GB is known to be enough, - 2GB might not allow you to build some packages. - Then go to the Microsoft Store and install Ubuntu (or another - Linux distribution). Start Ubuntu from the start menu. - - On the Linux running on WSL, you always have root access, so you - can use any of the installation methods described below for - Linux. + Enable Windows Subsystem for Linux (WSL) by following the + `official WSL setup guide + `_. Be + sure to do the steps to install WSL2 and set it as default. + Make sure to allocate enough RAM to WSL: 5GB is known to be enough, + 2GB might not allow you to build some packages. + Then go to the Microsoft Store and install Ubuntu (or another + Linux distribution). Start Ubuntu from the start menu. + + On the Linux running on WSL, you always have root access, so you + can use any of the installation methods described below for + Linux. Linux ===== From cd20de449411a2ddd3e36d1b76c03b08780b9e5d Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 13:11:22 +0000 Subject: [PATCH 087/191] Reintroduce traceback doctests after getting advice --- .../rings/finite_rings/hom_finite_field.pyx | 24 ++++++++++++++++++- .../finite_rings/hom_finite_field_givaro.pyx | 8 +++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 8f38408cda9..babac2568f4 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -35,6 +35,13 @@ map which is the inverse of `f` on the image of `f`:: Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T sage: g(f(t^3+t^2+1)) # random t^3 + t^2 + 1 + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^7 + To: Finite Field in T of size 3^21 + Defn: ... There is no embedding of `GF(5^6)` into `GF(5^11)`:: @@ -123,8 +130,16 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) + sage: g(f(t^3+t^2+1)) # random t^3 + t^2 + 1 + + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^7 + To: Finite Field in T of size 3^21 + Defn: t |--> ... """ for root, _ in x.minimal_polynomial().roots(ring=self.codomain()): if self._inverse(root) == x: @@ -360,6 +375,13 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^7 + To: Finite Field in T of size 3^21 + Defn: ... """ if self.base_map() is not None: raise NotImplementedError diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index 87281d41c64..75249339c6c 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -107,6 +107,14 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: g = f.section() sage: g(f(t+1)) # random t + 1 + + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^2 + To: Finite Field in T of size 3^4 + Defn: t |--> ... """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) From a5b5403d1b3f4794043712be418103ef44d8c552 Mon Sep 17 00:00:00 2001 From: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:14:44 +0000 Subject: [PATCH 088/191] Apply suggestions from code review Co-authored-by: Lorenz Panny <84067835+yyyyx4@users.noreply.github.com> --- .../rings/polynomial/polynomial_element.pyx | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e2742581a1c..5a20658a96d 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2052,11 +2052,11 @@ cdef class Polynomial(CommutativePolynomial): Helper function for any_irreducible_factor which computes the distinct degree factorisation of `self`. - Creates an iterator for all valid degrees `d`, and return + Creates an iterator for all valid degrees `d`, and returns tuples of the form `(a_d, d)` for a polynomial `a_d` the - product of irreducible polynomials of degree `d` + product of irreducible polynomials of degree `d`. - Assumes that self is squarefree. + Assumes that this polynomial is squarefree. EXAMPLES:: @@ -2096,9 +2096,8 @@ cdef class Polynomial(CommutativePolynomial): w = pow(w, q, v) ad = v.gcd(w - x) - yield (ad, d) - if not ad.is_one(): + yield (ad, d) v = v // ad w = w % v @@ -2111,10 +2110,10 @@ cdef class Polynomial(CommutativePolynomial): def _cantor_zassenhaus_split_to_irreducible(self, degree): """ - Helper function for any_irreducible_factor which computes + Helper function for :meth:`any_irreducible_factor` which computes a factor from a polynomial of the form `self = prod g_i(x)` - with all `g_i(x)` having the same degree. Uses the Cantor - Zassenhaus splitting method. + with all `g_i(x)` irreducible of the same degree. Uses the + Cantor-Zassenhaus splitting method. EXAMPLES:: @@ -2160,14 +2159,14 @@ cdef class Polynomial(CommutativePolynomial): return self # We expect to succeed with greater than 1/2 probability, - #so if we try 1000 times and fail, there's a bug somewhere. + # so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): # Sample a polynomial uniformly from R T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately if q % 2: - h = self.gcd(pow(T, (q-1)//2, self)-1) + h = self.gcd(pow(T, (q-1)//2, self) - 1) else: # Compute the trace of T with field of order 2^k # sum T^(2^i) for i in range (degree * k) @@ -2185,11 +2184,10 @@ cdef class Polynomial(CommutativePolynomial): return h # Else check if we have a non-trivial factor and keep going - if not hd.is_zero() and hd != self.degree(): - if 2*hd <= self.degree(): - return h._cantor_zassenhaus_split_to_irreducible(degree) - else: - return (self//h)._cantor_zassenhaus_split_to_irreducible(degree) + if 0 < hd < self.degree(): + if 2*hd > self.degree(): + h = self // h + return h._cantor_zassenhaus_split_to_irreducible(degree) # If you are reaching this error, chances are there's a bug in the code. raise AssertionError(f"no splitting of degree {degree} found for {self}") @@ -2201,7 +2199,7 @@ cdef class Polynomial(CommutativePolynomial): squarefree. Does this by first computing the distinct degree factorisations - of self and thenfinds a factor with Cantor-Zassenhaus + of self and then finds a factor with Cantor-Zassenhaus splitting. If degree is not None, then only irreducible factors of degree @@ -2369,7 +2367,7 @@ cdef class Polynomial(CommutativePolynomial): try: factorisation = self.factor() except (NotImplementedError, ValueError): - raise ValueError(f"Cannot factor {self} over the base ring {self.base_ring()}") + raise ValueError(f"cannot factor {self} over the base ring {self.base_ring()}") if degree is None: return factorisation[0][0] for (poly, e) in factorisation: @@ -2391,7 +2389,7 @@ cdef class Polynomial(CommutativePolynomial): # the user. # Initial checks for bad input - if self.degree().is_zero(): + if self.degree() <= 0: raise ValueError(f"there are no irreducible factors of {self}") # If we know the polynomial is square-free, we can start here @@ -2550,7 +2548,7 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: polynomial x^2 + 1 has no roots """ - # When not working over a finite field, do the simplecthing of factoring for + # When not working over a finite field, do the simple thing of factoring for # roots and picking the first root. If none are available, raise an error. from sage.categories.finite_fields import FiniteFields if not self.base_ring() in FiniteFields(): @@ -2583,7 +2581,7 @@ cdef class Polynomial(CommutativePolynomial): if degree < 0: from sage.misc.superseded import deprecation deprecation(37170, "negative ``degree`` will be disallowed. Instead use the bool `assume_distinct_deg`.") - degree = ZZ(abs(degree)) + degree = -degree assume_distinct_deg = True # If a certain degree is requested, then we find an irreducible factor of degree `degree` From 713ddf0110a32b8519576cecf0c771058702bc46 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:23:59 +0000 Subject: [PATCH 089/191] Add reviewer suggestions --- .../rings/polynomial/polynomial_element.pyx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 5a20658a96d..4a06cb79a96 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2099,7 +2099,6 @@ cdef class Polynomial(CommutativePolynomial): if not ad.is_one(): yield (ad, d) v = v // ad - w = w % v e = v.degree() @@ -2162,6 +2161,8 @@ cdef class Polynomial(CommutativePolynomial): # so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): # Sample a polynomial uniformly from R + # TODO: once #37118 has been merged, we can call + # R.random_monic_element(2*degree + 1) T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately @@ -2245,23 +2246,16 @@ cdef class Polynomial(CommutativePolynomial): # If the degree is not None we only want to check a single polynomial if degree is not None: for (poly, d) in self._distinct_degree_factorisation_squarefree(): - if d == degree and not poly.is_one(): + if d == degree: return poly._cantor_zassenhaus_split_to_irreducible(degree) # Stop iterating early if the degree is too large elif d > degree: raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") - # Otherwise we check all degrees, starting from the smallest + # Otherwise we use the smallest possible d value for (poly, d) in self._distinct_degree_factorisation_squarefree(): - # Skip the split checking if `_distinct_degree_factorisation_squarefree` - # has found no elements of degree `d` - if poly.is_one(): - continue - - # Otherwise find a factor from the distinct degree factor return poly._cantor_zassenhaus_split_to_irreducible(d) - raise ValueError(f"no irreducible factor could be computed from {self}") def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): @@ -2280,7 +2274,8 @@ cdef class Polynomial(CommutativePolynomial): - ``assume_distinct_deg`` (bool) -- Used for polynomials over finite fields. If ``True``, this polynomial is assumed to be - the product of irreducible polynomials of the same degree. + the product of irreducible polynomials of the same degree. If + this parameter is set to ``True`` then degree must also be set. EXAMPLES:: From 51b733c72667dab5f2fc9e94fa5d99fffbff6f0e Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:34:18 +0000 Subject: [PATCH 090/191] Reformat INPUT for docstring --- .../rings/polynomial/polynomial_element.pyx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4a06cb79a96..479c52a8123 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2264,18 +2264,18 @@ cdef class Polynomial(CommutativePolynomial): INPUT: - - ``degree`` (None or positive integer) -- Used for polynomials - over finite fields. Attempts to return an irreducible factor of - ``self`` of chosen degree ``degree``. - - - ``assume_squarefree`` (bool) -- Used for polynomials over - finite fields. If ``True``, this polynomial is assumed to be - squarefree. - - - ``assume_distinct_deg`` (bool) -- Used for polynomials over - finite fields. If ``True``, this polynomial is assumed to be - the product of irreducible polynomials of the same degree. If - this parameter is set to ``True`` then degree must also be set. + - ``degree`` (None or positive integer) -- (default: ``None``). + Used for polynomials over finite fields. Attempts to return an + irreducible factor of ``self`` of chosen degree ``degree``. + + - ``assume_squarefree`` (boolean) -- (default: ``False``). + Used for polynomials over finite fields. If ``True``, + this polynomial is assumed to be squarefree. + + - ``assume_distinct_deg`` (boolean) -- (default: ``False``). + Used for polynomials over finite fields. If ``True``, + this polynomial is assumed to be the product of irreducible + polynomials of degree equal to ``degree``. EXAMPLES:: From 89b8a4a29ed5dd0c83f7163f81bacb65275109e4 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:38:43 +0000 Subject: [PATCH 091/191] Clarify TODO about 37118 --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 479c52a8123..0daee97834a 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2162,7 +2162,7 @@ cdef class Polynomial(CommutativePolynomial): for _ in range(1000): # Sample a polynomial uniformly from R # TODO: once #37118 has been merged, we can call - # R.random_monic_element(2*degree + 1) + # R.random_element(degree=(2*degree+1), monic=True) T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately From bc13fa564461e5617a9298a08dd5c78b6740acf1 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:48:16 +0000 Subject: [PATCH 092/191] Clarify degree=None --- src/sage/rings/polynomial/polynomial_element.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 0daee97834a..bee696451dd 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2265,8 +2265,10 @@ cdef class Polynomial(CommutativePolynomial): INPUT: - ``degree`` (None or positive integer) -- (default: ``None``). - Used for polynomials over finite fields. Attempts to return an - irreducible factor of ``self`` of chosen degree ``degree``. + Used for polynomials over finite fields. If ``None``, returns + the the first factor found (usually the smallest). Otherwise, + attempts to return an irreducible factor of ``self`` of chosen + degree ``degree``. - ``assume_squarefree`` (boolean) -- (default: ``False``). Used for polynomials over finite fields. If ``True``, From 6b633fc2c118aff9ef71587ec94736f363b7aaba Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:49:54 +0000 Subject: [PATCH 093/191] Clean up examples --- src/sage/rings/polynomial/polynomial_element.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index bee696451dd..f02c60b5961 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2313,7 +2313,6 @@ cdef class Polynomial(CommutativePolynomial): Traceback (most recent call last): ... ValueError: no irreducible factor of degree 2 could be computed from x^4 + 162*x^3 + 7*x^2 + 154*x + 2 - sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) If we assume that the polynomial is product of irreducible polynomials of the same degree, we must also supply the degree:: @@ -2322,12 +2321,12 @@ cdef class Polynomial(CommutativePolynomial): sage: F = GF(163) sage: R. = F[] sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random + x + 98 sage: h.any_irreducible_factor(assume_distinct_deg=True) Traceback (most recent call last): ... ValueError: degree must be known if distinct degree factorisation is assumed - sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random - x + 98 Also works for extension fields and even characteristic:: From 32baddd6ff2cf43f5baeee8a26de68d360e62481 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 17:44:15 +0000 Subject: [PATCH 094/191] Clarify TODO in CZ function --- src/sage/rings/polynomial/polynomial_element.pyx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index f02c60b5961..7108c7b15f8 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2160,9 +2160,14 @@ cdef class Polynomial(CommutativePolynomial): # We expect to succeed with greater than 1/2 probability, # so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): - # Sample a polynomial uniformly from R - # TODO: once #37118 has been merged, we can call - # R.random_element(degree=(2*degree+1), monic=True) + # Sample a polynomial "uniformly" from R + # TODO: once #37118 has been merged, this can be made cleaner, + # as we will actually have access to uniform sampling. + # At the moment, we make an ugly call for polynomials of + # degree exactly 2*degree + 1, instead of polynomials of degree + # less than `self.degree()` as the original desc. of C-Z. + # Additionally, we can use the `monic` flag: + # R.random_element(degree=(self.degree() - 1), monic=True) T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately From e4e6a6f9e46bfc2a5bb4ff1b50c00b6a64517b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chenevi=C3=A8re?= Date: Thu, 1 Feb 2024 22:36:18 +0100 Subject: [PATCH 095/191] added a dash --- src/doc/en/reference/references/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 335bc1233f8..5e98370e98f 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -508,7 +508,7 @@ REFERENCES: Springer, 2012, http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf -.. [BMFPR2011] \M. Bousquet-Melou, É. Fusy, L.-F. Préville Ratelle, +.. [BMFPR2011] \M. Bousquet-Melou, É. Fusy, L.-F. Préville-Ratelle, *The number of intervals in the m-Tamari lattices*. Electronic Journal of Combinatorics 18(2), 2011. :doi:`10.37236/2027` From adbccdcfe21a6a27c3201e7e65fea62d1b112cb9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 2 Feb 2024 16:24:32 +0900 Subject: [PATCH 096/191] Removing an superfluous comment. --- .../categories/finite_dimensional_lie_algebras_with_basis.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 74dc3d1e2a5..ee3c14948aa 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -1287,7 +1287,6 @@ def compute_diff(k): """ # The indices for the exterior algebra ext_ind = {tuple(X): i for i, X in enumerate(combinations(LI, k-1))} - # The indices for the tensor product # Compute the part independent of the module first ("part 2" of the computation) if sparse: From 31fca5d9d8ac388c529d1459e75e674da8773fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chenevi=C3=A8re?= Date: Fri, 2 Feb 2024 11:27:25 +0100 Subject: [PATCH 097/191] minifix ref --- src/sage/combinat/tamari_lattices.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 7f7b217c5d0..3c493a9dad6 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -215,9 +215,11 @@ def GeneralizedTamariLattice(a, b, m=1): REFERENCES: - [BMFPR2011]_ - [PRV2017]_ - [CC2023]_ + - [BMFPR2011]_ + + - [PRV2017]_ + + - [CC2023]_ """ if a < b * m: raise ValueError("the condition a>=b*m does not hold") From e1031827ed0f31fa95792d92949f05b9fe7c08d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chenevi=C3=A8re?= Date: Fri, 2 Feb 2024 11:43:58 +0100 Subject: [PATCH 098/191] removed spaces on empty lines --- src/sage/combinat/tamari_lattices.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 3c493a9dad6..b84b587e1b3 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -216,9 +216,9 @@ def GeneralizedTamariLattice(a, b, m=1): REFERENCES: - [BMFPR2011]_ - + - [PRV2017]_ - + - [CC2023]_ """ if a < b * m: From ba8eeef254ac50521cd8b1c28575e68df0199a2b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Fri, 2 Feb 2024 13:18:02 +0000 Subject: [PATCH 099/191] Fix failing doctests --- src/sage/rings/finite_rings/homset.py | 2 +- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/finite_rings/homset.py b/src/sage/rings/finite_rings/homset.py index f9d523199ec..5c122a1c6b7 100644 --- a/src/sage/rings/finite_rings/homset.py +++ b/src/sage/rings/finite_rings/homset.py @@ -334,7 +334,7 @@ def _an_element_(self): TESTS:: - sage: Hom(GF(3^3, 'a'), GF(3^6, 'b')).an_element() + sage: Hom(GF(3^3, 'a'), GF(3^6, 'b')).an_element() # random Ring morphism: From: Finite Field in a of size 3^3 To: Finite Field in b of size 3^6 diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 7108c7b15f8..b749a379d40 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2065,7 +2065,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R. = F[] sage: f = (x + 162) * (x^3 + 7*x + 161) * (x^7 + 9*x + 161) sage: list(f._distinct_degree_factorisation_squarefree()) - [(x + 162, 1), (1, 2), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] + [(x + 162, 1), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] :: From efa4c77a81f99ed22b46666f378dd0b8a4534293 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Fri, 2 Feb 2024 14:05:42 +0000 Subject: [PATCH 100/191] remove invariants commands in global namespace #28064 --- src/sage/schemes/hyperelliptic_curves/all.py | 41 -------------------- 1 file changed, 41 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/all.py b/src/sage/schemes/hyperelliptic_curves/all.py index ff4d929e891..88733235129 100644 --- a/src/sage/schemes/hyperelliptic_curves/all.py +++ b/src/sage/schemes/hyperelliptic_curves/all.py @@ -1,45 +1,4 @@ -""" -Tests for deprecations of imports in global namespace from :trac:`28064`:: - - sage: igusa_clebsch_invariants - doctest:warning...: - DeprecationWarning: - Importing igusa_clebsch_invariants from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import igusa_clebsch_invariants" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... - - sage: absolute_igusa_invariants_kohel - doctest:warning...: - DeprecationWarning: - Importing absolute_igusa_invariants_kohel from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import absolute_igusa_invariants_kohel" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... - - sage: absolute_igusa_invariants_wamelen - doctest:warning...: - DeprecationWarning: - Importing absolute_igusa_invariants_wamelen from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import absolute_igusa_invariants_wamelen" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... - - sage: clebsch_invariants - doctest:warning...: - DeprecationWarning: - Importing clebsch_invariants from here is deprecated; - please use "from sage.schemes.hyperelliptic_curves.invariants import clebsch_invariants" instead. - See https://github.com/sagemath/sage/issues/28064 for details. - ... -""" -from sage.misc.lazy_import import lazy_import - from .constructor import HyperellipticCurve from .kummer_surface import KummerSurface -lazy_import('sage.schemes.hyperelliptic_curves.invariants', - ['igusa_clebsch_invariants', 'absolute_igusa_invariants_kohel', - 'absolute_igusa_invariants_wamelen', 'clebsch_invariants'], - deprecation=28064) from .mestre import (Mestre_conic, HyperellipticCurve_from_invariants) from . import monsky_washnitzer From 134e16d9ffd93b2f24d38369fdfea8901b67fa13 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 2 Feb 2024 18:53:47 +0100 Subject: [PATCH 101/191] add reference for "minimal element is generator" claim --- src/sage/algebras/quatalg/quaternion_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 45aa3579377..6c8619de286 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -2718,7 +2718,7 @@ def minimal_element(self): Return an element in this quaternion ideal of minimal norm. If the ideal is a principal lattice, this method can be used - to find a generator. + to find a generator; see [Piz1980]_, Corollary 1.20. EXAMPLES:: From 9e04861e71595d90dcec3b743673870f9a6c74c2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Feb 2024 13:38:01 -0800 Subject: [PATCH 102/191] .github/workflows/ci-linux-incremental.yml: Do not require labels to be set --- .github/workflows/ci-linux-incremental.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 451560ecd8c..77f40586212 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -17,13 +17,6 @@ name: CI Linux incremental on: pull_request: - types: - # Defaults - - opened - - synchronize - - reopened - # When a CI label is added - - labeled paths: - 'build/pkgs/**' - 'pkgs/**' @@ -81,14 +74,6 @@ jobs: test: needs: [changed_files] - if: | - github.event_name != 'pull_request' || - ((github.event.action != 'labeled' && - (contains(github.event.pull_request.labels.*.name, 'c: packages: standard') || - contains(github.event.pull_request.labels.*.name, 'c: packages: optional'))) || - (github.event.action == 'labeled' && - (github.event.label.name == 'c: packages: optional' || - github.event.label.name == 'c: packages: standard'))) uses: ./.github/workflows/docker.yml with: # Build incrementally from published Docker image From 1310cfd4be14634d0879c7deebd8b4edf54726ec Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Feb 2024 13:38:25 -0800 Subject: [PATCH 103/191] .github/workflows/ci-linux-incremental.yml: Update tj-actions/changed-files to v42 --- .github/workflows/ci-linux-incremental.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 77f40586212..61a50c2c66e 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - name: Get all packages that have changed id: changed-packages - uses: tj-actions/changed-files@v41 + uses: tj-actions/changed-files@v42 with: files_yaml: | configures: From c2bb4746970467da27b2022634c873b8f260ba29 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Jan 2024 19:18:20 -0800 Subject: [PATCH 104/191] Make src/sage/tests/books a package directory --- src/sage/tests/books/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/sage/tests/books/__init__.py diff --git a/src/sage/tests/books/__init__.py b/src/sage/tests/books/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 5ad42e8a485ccefc22f6d37236fdb699ab752883 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 2 Jan 2024 18:18:25 -0800 Subject: [PATCH 105/191] build/pkgs/givaro/spkg-configure.m4: Switch to using pkg-config --- build/pkgs/givaro/spkg-configure.m4 | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/build/pkgs/givaro/spkg-configure.m4 b/build/pkgs/givaro/spkg-configure.m4 index 84c4af52087..9fdc269bab1 100644 --- a/build/pkgs/givaro/spkg-configure.m4 +++ b/build/pkgs/givaro/spkg-configure.m4 @@ -1,25 +1,6 @@ SAGE_SPKG_CONFIGURE([givaro], [ - m4_pushdef([SAGE_GIVARO_MINVER],["40101"]) - m4_pushdef([SAGE_GIVARO_LTVER],["40200"]) - SAGE_SPKG_DEPCHECK([gmp], [ - AC_PATH_PROG([GIVAROCONFIG], [givaro-config]) - AS_IF([test x$GIVAROCONFIG = x], [ - AC_MSG_NOTICE([givaro-config not found. Installing givaro]) - sage_spkg_install_givaro=yes], [ - AC_MSG_CHECKING([is givaro's version acceptable? ]) - givaro_ver=`$GIVAROCONFIG --decimal-version 2>> config.log` - AX_COMPARE_VERSION([$givaro_ver], [ge], SAGE_GIVARO_MINVER, [ - AX_COMPARE_VERSION([$givaro_ver], [lt], SAGE_GIVARO_LTVER, [ - AC_MSG_RESULT([yes])], [ - AC_MSG_RESULT([no, too new]) - sage_spkg_install_givaro=yes - ]) - ], [ - AC_MSG_RESULT([no, too old]) - sage_spkg_install_givaro=yes - ]) - ]) - ]) - m4_popdef([SAGE_GIVARO_LTVER]) - m4_popdef([SAGE_GIVARO_MINVER]) + PKG_CHECK_MODULES([GIVARO], + [givaro >= 4.1.1], + [sage_spkg_install_givaro=no], + [sage_spkg_install_givaro=yes]) ]) From db7703c47b27ff12e5a2448132ebbe602f5922d8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 2 Jan 2024 18:54:05 -0800 Subject: [PATCH 106/191] build/pkgs/{givaro,fflas_ffpack,linbox}: Accept matching sets of old and new versions --- build/pkgs/fflas_ffpack/spkg-configure.m4 | 2 +- build/pkgs/givaro/spkg-configure.m4 | 2 +- build/pkgs/linbox/spkg-configure.m4 | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/build/pkgs/fflas_ffpack/spkg-configure.m4 b/build/pkgs/fflas_ffpack/spkg-configure.m4 index 01a04713f07..646cfb527e4 100644 --- a/build/pkgs/fflas_ffpack/spkg-configure.m4 +++ b/build/pkgs/fflas_ffpack/spkg-configure.m4 @@ -4,7 +4,7 @@ SAGE_SPKG_CONFIGURE([fflas_ffpack], [ # the system fflas-ffpack, too. Use pkg-config to find a # recentish version, if there is one. PKG_CHECK_MODULES([FFLAS_FFPACK], - [fflas-ffpack >= 2.4.0], + [fflas-ffpack >= 2.4.0],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_fflas_ffpack=no], [sage_spkg_install_fflas_ffpack=yes]) ]) diff --git a/build/pkgs/givaro/spkg-configure.m4 b/build/pkgs/givaro/spkg-configure.m4 index 9fdc269bab1..36cfab37081 100644 --- a/build/pkgs/givaro/spkg-configure.m4 +++ b/build/pkgs/givaro/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([givaro], [ PKG_CHECK_MODULES([GIVARO], - [givaro >= 4.1.1], + [givaro >= 4.1.1],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_givaro=no], [sage_spkg_install_givaro=yes]) ]) diff --git a/build/pkgs/linbox/spkg-configure.m4 b/build/pkgs/linbox/spkg-configure.m4 index 1571c9de790..8c5b85ef7fd 100644 --- a/build/pkgs/linbox/spkg-configure.m4 +++ b/build/pkgs/linbox/spkg-configure.m4 @@ -1,8 +1,16 @@ SAGE_SPKG_CONFIGURE([linbox], [ SAGE_SPKG_DEPCHECK([fflas_ffpack flint fplll givaro gmp iml m4ri m4rie mpfr ntl], [ - PKG_CHECK_MODULES([LINBOX], - [linbox >= 1.6.3 linbox <= 1.6.4], - [sage_spkg_install_linbox=no], - [sage_spkg_install_linbox=yes]) + PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching old versions + [linbox >= 1.6.3 linbox <= 1.6.4 fflas-ffpack >= 2.4.0 fflas-ffpack < 2.5.0 givaro >= 4.1.1 givaro < 4.2.0], + [sage_spkg_install_linbox=no], + [PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching new versions + [linbox >= 1.7.0 linbox <= 1.7.0 fflas-ffpack >= 2.5.0 givaro >= 4.2.0 givaro < 4.3.0], + [sage_spkg_install_linbox=no], + [sage_spkg_install_linbox=yes])]) ]) +], [dnl REQUIRED_CHECK +], [dnl PRE +], [dnl POST + sage_spkg_install_fflas_ffpack=$sage_spkg_install_linbox + sage_spkg_install_givaro=$sage_spkg_install_linbox ]) From 9699e4cb50968a8099044cf2616ccd4da43bc13b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 3 Jan 2024 10:10:02 -0800 Subject: [PATCH 107/191] .github/workflows/ci-linux-incremental.yml: Fix name of archlinux --- .github/workflows/ci-linux-incremental.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 451560ecd8c..96ed2fb4bcd 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -139,7 +139,7 @@ jobs: # with only a small number of test failures as of 10.2.rc0 tox_system_factors: >- ["gentoo-python3.11", - "archlinux"] + "archlinux-latest"] tox_packages_factors: >- ["standard-sitepackages"] docker_push_repository: ghcr.io/${{ github.repository }}/ From 54f7921bbbe1706dff2ae3e9f9d14e090c5c18ec Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 16 Jan 2024 08:58:54 -0800 Subject: [PATCH 108/191] tox.ini (fedora): Add fedora-40 --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 6a70574b223..33f0b433097 100644 --- a/tox.ini +++ b/tox.ini @@ -266,7 +266,7 @@ setenv = linuxmint-21.2: BASE_IMAGE=linuxmintd/mint21.2 # # https://hub.docker.com/_/fedora - # as of 2023-05, latest=38, rawhide=39 + # as of 2024-01, latest=39, rawhide=40 fedora: SYSTEM=fedora fedora: BASE_IMAGE=fedora fedora-26: BASE_TAG=26 @@ -294,6 +294,8 @@ setenv = fedora-38: IGNORE_MISSING_SYSTEM_PACKAGES=yes fedora-39: BASE_TAG=39 fedora-39: IGNORE_MISSING_SYSTEM_PACKAGES=yes + fedora-40: BASE_TAG=40 + fedora-40: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/scientificlinux/sl # From c01da05effa34295939da5dd9992cffb9e1d13c5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 16 Jan 2024 08:59:48 -0800 Subject: [PATCH 109/191] tox.ini (fedora): fedora-{34,35,36} have no gnu-free-fonts package, so do not set IGNORE_MISSING_SYSTEM_PACKAGES=no --- tox.ini | 3 --- 1 file changed, 3 deletions(-) diff --git a/tox.ini b/tox.ini index 33f0b433097..3673833724c 100644 --- a/tox.ini +++ b/tox.ini @@ -283,11 +283,8 @@ setenv = fedora-32: BASE_TAG=32 fedora-33: BASE_TAG=33 fedora-34: BASE_TAG=34 - fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-35: BASE_TAG=35 - fedora-35: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-36: BASE_TAG=36 - fedora-36: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-37: BASE_TAG=37 fedora-37: IGNORE_MISSING_SYSTEM_PACKAGES=yes fedora-38: BASE_TAG=38 From 21c0a61b6bf43ab9cd910b39742fa386aec9c365 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 16 Jan 2024 10:10:37 -0800 Subject: [PATCH 110/191] build/pkgs/free_fonts: Fix fedora.txt --- build/pkgs/free_fonts/distros/fedora.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/pkgs/free_fonts/distros/fedora.txt b/build/pkgs/free_fonts/distros/fedora.txt index b03a4525c08..435c13a9578 100644 --- a/build/pkgs/free_fonts/distros/fedora.txt +++ b/build/pkgs/free_fonts/distros/fedora.txt @@ -1 +1,3 @@ -gnu-free-fonts +gnu-free-mono-fonts +gnu-free-sans-fonts +gnu-free-serif-fonts From a116ed7720556467ca1e4a8f2a0f350478a9e602 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 16 Jan 2024 10:30:29 -0800 Subject: [PATCH 111/191] build/pkgs/free_fonts/distros/repology.txt: Update --- build/pkgs/free_fonts/distros/repology.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/pkgs/free_fonts/distros/repology.txt b/build/pkgs/free_fonts/distros/repology.txt index 0870af23017..30e8a613e7e 100644 --- a/build/pkgs/free_fonts/distros/repology.txt +++ b/build/pkgs/free_fonts/distros/repology.txt @@ -1 +1,4 @@ -gnu-freefont +font-freefont +fonts:gnu-freefont +texlive:gnu-freefont +# https://repology.org/project/freefonts/versions is something else. From b7032a1cb57ee744fb03d3994baf44039d07ce27 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 16 Jan 2024 10:35:50 -0800 Subject: [PATCH 112/191] build/pkgs/free_fonts: Add to distros --- build/pkgs/free_fonts/distros/nix.txt | 1 + build/pkgs/free_fonts/distros/openbsd.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 build/pkgs/free_fonts/distros/nix.txt create mode 100644 build/pkgs/free_fonts/distros/openbsd.txt diff --git a/build/pkgs/free_fonts/distros/nix.txt b/build/pkgs/free_fonts/distros/nix.txt new file mode 100644 index 00000000000..d5a8b479117 --- /dev/null +++ b/build/pkgs/free_fonts/distros/nix.txt @@ -0,0 +1 @@ +freefont-ttf diff --git a/build/pkgs/free_fonts/distros/openbsd.txt b/build/pkgs/free_fonts/distros/openbsd.txt new file mode 100644 index 00000000000..05a692834fe --- /dev/null +++ b/build/pkgs/free_fonts/distros/openbsd.txt @@ -0,0 +1 @@ +fonts/freefont-ttf From 39f17226d9e57c238cc093e5c77edaec2bd7315f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Feb 2024 11:21:08 +0100 Subject: [PATCH 113/191] some pep8 fixes in rings, and activate E221 --- .../function_field/function_field_polymod.py | 69 ++++++++++--------- .../function_field/function_field_rational.py | 21 +++--- .../rings/function_field/ideal_polymod.py | 54 +++++++-------- .../rings/function_field/order_polymod.py | 6 +- .../rings/function_field/place_polymod.py | 2 +- .../rings/number_field/number_field_ideal.py | 6 +- src/sage/rings/number_field/selmer_group.py | 6 +- .../polynomial/multi_polynomial_ideal.py | 4 +- src/sage/rings/polynomial/pbori/ll.py | 2 +- src/sage/rings/polynomial/term_order.py | 10 +-- src/sage/rings/quotient_ring_element.py | 2 +- src/tox.ini | 2 +- 12 files changed, 93 insertions(+), 91 deletions(-) diff --git a/src/sage/rings/function_field/function_field_polymod.py b/src/sage/rings/function_field/function_field_polymod.py index b740b771bec..44c1390bc86 100644 --- a/src/sage/rings/function_field/function_field_polymod.py +++ b/src/sage/rings/function_field/function_field_polymod.py @@ -418,10 +418,11 @@ def monic_integral_model(self, names=None): raise ValueError("names must contain at most 2 entries") if self.base_field() is not self.rational_function_field(): - L,from_L,to_L = self.simple_model() - ret,ret_to_L,L_to_ret = L.monic_integral_model(names) - from_ret = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) - to_ret = self.hom( [L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())] ) + L, from_L, to_L = self.simple_model() + ret, ret_to_L, L_to_ret = L.monic_integral_model(names) + from_ret = ret.hom([from_L(ret_to_L(ret.gen())), + from_L(ret_to_L(ret.base_field().gen()))]) + to_ret = self.hom([L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())]) return ret, from_ret, to_ret else: if self.polynomial().is_monic() and all(c.denominator().is_one() for c in self.polynomial()): @@ -431,12 +432,12 @@ def monic_integral_model(self, names=None): return self.change_variable_name(names) else: if not names: - names = (self.variable_name()+"_",) + names = (self.variable_name() + "_",) if len(names) == 1: names = (names[0], self.rational_function_field().variable_name()) g, d = self._make_monic_integral(self.polynomial()) - K,from_K,to_K = self.base_field().change_variable_name(names[1]) + K, from_K, to_K = self.base_field().change_variable_name(names[1]) g = g.map_coefficients(to_K) ret = K.extension(g, names=names[0]) from_ret = ret.hom([self.gen() * d, self.base_field().gen()]) @@ -791,7 +792,7 @@ def free_module(self, base=None, basis=None, map=True): if not map: return V from_V = MapVectorSpaceToFunctionField(V, self) - to_V = MapFunctionFieldToVectorSpace(self, V) + to_V = MapFunctionFieldToVectorSpace(self, V) return (V, from_V, to_V) def maximal_order(self): @@ -982,7 +983,7 @@ def hom(self, im_gens, base_morphism=None): yy |--> y """ - if not isinstance(im_gens, (list,tuple)): + if not isinstance(im_gens, (list, tuple)): im_gens = [im_gens] if len(im_gens) == 0: raise ValueError("no images specified") @@ -1025,11 +1026,11 @@ def genus(self): # making the auxiliary ring which only has polynomials # with integral coefficients. tmpAuxRing = PolynomialRing(self._base_field.constant_field(), - str(self._base_field.gen())+','+str(self._ring.gen())) + str(self._base_field.gen()) + ',' + str(self._ring.gen())) intMinPoly, d = self._make_monic_integral(self._polynomial) curveIdeal = tmpAuxRing.ideal(intMinPoly) - singular.lib('normal.lib') #loading genus method in Singular + singular.lib('normal.lib') # loading genus method in Singular return int(curveIdeal._singular_().genus()) else: @@ -1170,7 +1171,7 @@ def _simple_model(self, name='v'): B = MS(B) M_b = V_to_N(B.solve_left(M_to_V(b))) M_a = V_to_N(B.solve_left(M_to_V(a))) - M_to_N = M.hom([M_a,M_b]) + M_to_N = M.hom([M_a, M_b]) return N, N_to_M, M_to_N @@ -1285,7 +1286,7 @@ def simple_model(self, name=None): if isinstance(self.base_field(), RationalFunctionField): # the extension is simple already if name == self.variable_name(): - id = Hom(self,self).identity() + id = Hom(self, self).identity() return self, id, id else: ret = self.base_field().extension(self.polynomial(), names=(name,)) @@ -1299,8 +1300,8 @@ def simple_model(self, name=None): self_ = base_.extension(self.polynomial().map_coefficients(to_base_), names=(name,)) gens_in_base_ = [to_base_(k.gen()) for k in base._intermediate_fields(base.rational_function_field())] - to_self_ = self.hom([self_.gen()]+gens_in_base_) - from_self_ = self_.hom([self.gen(),from_base_(base_.gen())]) + to_self_ = self.hom([self_.gen()] + gens_in_base_) + from_self_ = self_.hom([self.gen(), from_base_(base_.gen())]) # now collapse self_/base_/K(x) ret, ret_to_self_, self__to_ret = self_._simple_model(name) @@ -1549,8 +1550,8 @@ def separable_model(self, names=None): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(self.constant_base_field(), names=names) S = R.remove_var(names[1]) - f = R( L.polynomial().change_variable_name(names[1]).map_coefficients( - lambda c:c.numerator().change_variable_name(names[0]), S)) + f = R(L.polynomial().change_variable_name(names[1]).map_coefficients( + lambda c: c.numerator().change_variable_name(names[0]), S)) f = f.polynomial(R.gen(0)).change_ring(K) f /= f.leading_coefficient() # f must be separable in the other variable (otherwise it would factor) @@ -1558,11 +1559,11 @@ def separable_model(self, names=None): ret = K.extension(f, names=(names[0],)) # isomorphisms between L and ret are given by swapping generators - ret_to_L = ret.hom( [L(L.base_field().gen()), L.gen()] ) - L_to_ret = L.hom( [ret(K.gen()), ret.gen()] ) + ret_to_L = ret.hom([L(L.base_field().gen()), L.gen()]) + L_to_ret = L.hom([ret(K.gen()), ret.gen()]) # compose with from_L and to_L to get the desired isomorphisms between self and ret - f = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) - t = self.hom( [L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))] ) + f = ret.hom([from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))]) + t = self.hom([L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))]) return ret, f, t def change_variable_name(self, name): @@ -1637,13 +1638,13 @@ def change_variable_name(self, name): raise ValueError("name must contain at least one string") elif len(name) == 1: base = self.base_field() - from_base = to_base = Hom(base,base).identity() + from_base = to_base = Hom(base, base).identity() else: base, from_base, to_base = self.base_field().change_variable_name(name[1:]) ret = base.extension(self.polynomial().map_coefficients(to_base), names=(name[0],)) - f = ret.hom( [k.gen() for k in self._intermediate_fields(self.rational_function_field())] ) - t = self.hom( [k.gen() for k in ret._intermediate_fields(ret.rational_function_field())] ) + f = ret.hom([k.gen() for k in self._intermediate_fields(self.rational_function_field())]) + t = self.hom([k.gen() for k in ret._intermediate_fields(ret.rational_function_field())]) return ret, f, t @@ -1701,7 +1702,7 @@ def _inversion_isomorphism(self): x |--> x) """ K = self.base_field() - R = PolynomialRing(K,'T') + R = PolynomialRing(K, 'T') x = K.gen() xinv = 1/x @@ -1709,8 +1710,8 @@ def _inversion_isomorphism(self): F_poly = R([h(c) for c in self.polynomial().list()]) F = K.extension(F_poly) - self2F = self.hom([F.gen(),xinv]) - F2self = F.hom([self.gen(),xinv]) + self2F = self.hom([F.gen(), xinv]) + F2self = F.hom([self.gen(), xinv]) M, M2F, F2M = F.monic_integral_model('s') @@ -1844,7 +1845,7 @@ def genus(self): The genus is computed by the Hurwitz genus formula. """ k, _ = self.exact_constant_field() - different_degree = self.different().degree() # must be even + different_degree = self.different().degree() # must be even return Integer(different_degree // 2 - self.degree() / k.degree()) + 1 def residue_field(self, place, name=None): @@ -2123,7 +2124,7 @@ def _places_finite(self, degree): for d in degree.divisors(): for p in K._places_finite(degree=d): - for prime,_,_ in O.decomposition(p.prime_ideal()): + for prime, _, _ in O.decomposition(p.prime_ideal()): place = prime.place() if place.degree() == degree: yield place @@ -2167,7 +2168,7 @@ def _places_infinite(self, degree): """ Oinf = self.maximal_order_infinite() - for prime,_,_ in Oinf.decomposition(): + for prime, _, _ in Oinf.decomposition(): place = prime.place() if place.degree() == degree: yield place @@ -2312,7 +2313,7 @@ def number_of_rational_places(self, r=1): L = self.L_polynomial() Lp = L.derivative() - R = IntegerRing()[[L.parent().gen()]] # power series ring + R = IntegerRing()[[L.parent().gen()]] # power series ring f = R(Lp / L, prec=r) n = f[r-1] + q**r + 1 @@ -2408,13 +2409,13 @@ def _maximal_order_basis(self): from .hermite_form_polynomial import reversed_hermite_form k = self.constant_base_field() - K = self.base_field() # rational function field + K = self.base_field() # rational function field n = self.degree() # Construct the defining polynomial of the function field as a # two-variate polynomial g in the ring k[y,x] where k is the constant # base field. - S,(y,x) = PolynomialRing(k, names='y,x', order='lex').objgens() + S, (y, x) = PolynomialRing(k, names='y,x', order='lex').objgens() v = self.polynomial().list() g = sum([v[i].numerator().subs(x) * y**i for i in range(len(v))]) @@ -2431,7 +2432,7 @@ def _maximal_order_basis(self): gflat = R.zero() for m in g.monomials(): c = g.monomial_coefficient(m).polynomial('zz') - gflat += R(c) * R(m) # R(m) is a monomial in yy and xx + gflat += R(c) * R(m) # R(m) is a monomial in yy and xx k_poly = R(k.polynomial('zz')) @@ -2439,7 +2440,7 @@ def _maximal_order_basis(self): pols_in_R = normalize(R.ideal([k_poly, gflat])) # reconstruct polynomials in S - h = R.hom([y,x,k.gen()],S) + h = R.hom([y, x, k.gen()], S) pols_in_S = [h(f) for f in pols_in_R] else: # Call Singular. Singular's "normal" function returns a basis diff --git a/src/sage/rings/function_field/function_field_rational.py b/src/sage/rings/function_field/function_field_rational.py index 226d87d084c..8a0a6f25764 100644 --- a/src/sage/rings/function_field/function_field_rational.py +++ b/src/sage/rings/function_field/function_field_rational.py @@ -323,7 +323,7 @@ def _to_polynomial(self, f): """ K = f.parent().constant_base_field() if f.denominator() in K: - return f.numerator()/K(f.denominator()) + return f.numerator() / K(f.denominator()) raise ValueError("only polynomials can be converted to the underlying polynomial ring") def _to_bivariate_polynomial(self, f): @@ -350,7 +350,8 @@ def _to_bivariate_polynomial(self, f): v = f.list() denom = lcm([a.denominator() for a in v]) S = denom.parent() - x,t = S.base_ring()['%s,%s' % (f.parent().variable_name(),self.variable_name())].gens() + x, t = S.base_ring()['%s,%s' % (f.parent().variable_name(), + self.variable_name())].gens() phi = S.hom([t]) return sum([phi((denom * v[i]).numerator()) * x**i for i in range(len(v))]), denom @@ -420,12 +421,12 @@ def _factor_univariate_polynomial(self, f, proof=None): x = f.parent().gen() t = f.parent().base_ring().gen() phi = F.parent().hom([x, t]) - v = [(phi(P),e) for P, e in fac] - unit = phi(fac.unit())/d + v = [(phi(P), e) for P, e in fac] + unit = phi(fac.unit()) / d w = [] for a, e in v: c = a.leading_coefficient() - a = a/c + a = a / c # undo any variable substitution that we introduced for the bivariate polynomial if old_variable_name != a.variable_name(): a = a.change_variable_name(old_variable_name) @@ -433,7 +434,7 @@ def _factor_univariate_polynomial(self, f, proof=None): if a.is_unit(): unit *= a**e else: - w.append((a,e)) + w.append((a, e)) from sage.structure.factorization import Factorization return Factorization(w, unit=unit) @@ -553,7 +554,7 @@ def free_module(self, base=None, basis=None, map=True): if not map: return V from_V = MapVectorSpaceToFunctionField(V, self) - to_V = MapFunctionFieldToVectorSpace(self, V) + to_V = MapFunctionFieldToVectorSpace(self, V) return (V, from_V, to_V) def random_element(self, *args, **kwds): @@ -683,7 +684,7 @@ def hom(self, im_gens, base_morphism=None): """ if isinstance(im_gens, CategoryObject): return self.Hom(im_gens).natural_map() - if not isinstance(im_gens, (list,tuple)): + if not isinstance(im_gens, (list, tuple)): im_gens = [im_gens] if len(im_gens) != 1: raise ValueError("there must be exactly one generator") @@ -834,8 +835,8 @@ def change_variable_name(self, name): raise ValueError("names must be a tuple with a single string") name = name[0] if name == self.variable_name(): - id = Hom(self,self).identity() - return self,id,id + id = Hom(self, self).identity() + return self, id, id else: from .constructor import FunctionField ret = FunctionField(self.constant_base_field(), name) diff --git a/src/sage/rings/function_field/ideal_polymod.py b/src/sage/rings/function_field/ideal_polymod.py index 6735dd6a8fa..da45bc508cf 100644 --- a/src/sage/rings/function_field/ideal_polymod.py +++ b/src/sage/rings/function_field/ideal_polymod.py @@ -27,6 +27,7 @@ from .ideal import FunctionFieldIdeal, FunctionFieldIdealInfinite + class FunctionFieldIdeal_polymod(FunctionFieldIdeal): """ Fractional ideals of algebraic function fields @@ -407,7 +408,7 @@ def _mul_(self, other): g1, g2 = other._gens_two_vecs vecs = list(g1 * self._hnf) + [mul(g2, v) for v in self._hnf] else: - vecs = [mul(r1,r2) for r1 in self._hnf for r2 in other._hnf] + vecs = [mul(r1, r2) for r1 in self._hnf for r2 in other._hnf] return O._ideal_from_vectors_and_denominator(vecs, self._denominator * other._denominator) @@ -488,7 +489,7 @@ def intersect(self, other): n = A.ncols() # intersect the row spaces of A and B - M = block_matrix([[I,I],[A,O],[O,B]]) + M = block_matrix([[I, I], [A, O], [O, B]]) # reversed Hermite form U = reversed_hermite_form(M, transformation=True) @@ -601,7 +602,7 @@ def gens_over_base(self): sage: I.gens_over_base() (x^3 + 1, y + x) """ - gens, d = self._gens_over_base + gens, d = self._gens_over_base return tuple([~d * b for b in gens]) @lazy_attribute @@ -620,9 +621,8 @@ def _gens_over_base(self): sage: I._gens_over_base ([x, y], x) """ - gens = [] - for row in self._hnf: - gens.append(sum([c1*c2 for c1,c2 in zip(row, self._ring.basis())])) + gens = [sum([c1 * c2 for c1, c2 in zip(row, self._ring.basis())]) + for row in self._hnf] return gens, self._denominator def gens(self): @@ -864,7 +864,7 @@ def is_prime(self): [True, True] """ factors = self.factor() - if len(factors) == 1 and factors[0][1] == 1: # prime! + if len(factors) == 1 and factors[0][1] == 1: # prime! prime = factors[0][0] assert self == prime self._relative_degree = prime._relative_degree @@ -1013,13 +1013,13 @@ def _factor(self): i = d * self factors = [] - primes = set([o.ideal(p) for p,_ in d.factor()] + [p for p,_ in i.ideal_below().factor()]) + primes = set([o.ideal(p) for p, _ in d.factor()] + [p for p, _ in i.ideal_below().factor()]) for prime in primes: qs = [q[0] for q in O.decomposition(prime)] for q in qs: exp = q.valuation(self) if exp != 0: - factors.append((q,exp)) + factors.append((q, exp)) return factors @@ -1100,10 +1100,10 @@ def __pow__(self, mod): J = [ppow * v for v in I] else: p, q = self._gens_two_vecs - q = sum(e1 * e2 for e1,e2 in zip(O.basis(), q)) + q = sum(e1 * e2 for e1, e2 in zip(O.basis(), q)) ppow = p**mod qpow = O._coordinate_vector(q**mod) - J = [ppow * v for v in I] + [mul(qpow,v) for v in I] + J = [ppow * v for v in I] + [mul(qpow, v) for v in I] return O._ideal_from_vectors_and_denominator(J, self._denominator**mod) @@ -1171,7 +1171,7 @@ def gens_two(self): of Function field in y defined by y^2 + y + (x^2 + 1)/x """ d = self.denominator() - return tuple(e/d for e in self._gens_two()) + return tuple(e / d for e in self._gens_two()) @cached_method def _gens_two(self): @@ -1219,18 +1219,18 @@ def _gens_two(self): O = self.ring() F = O.fraction_field() - if self._kummer_form is not None: # prime ideal + if self._kummer_form is not None: # prime ideal _g1, _g2 = self._kummer_form g1 = F(_g1) - g2 = sum([c1*c2 for c1,c2 in zip(_g2, O.basis())]) + g2 = sum([c1 * c2 for c1, c2 in zip(_g2, O.basis())]) if g2: self._gens_two_vecs = (_g1, _g2) - return (g1,g2) + return (g1, g2) else: self._gens_two_vecs = (_g1,) return (g1,) - ### start to search for two generators + # ---- start to search for two generators hnf = self._hnf @@ -1238,7 +1238,7 @@ def _gens_two(self): for e in hnf.diagonal(): norm *= e - if norm.is_constant(): # unit ideal + if norm.is_constant(): # unit ideal self._gens_two_vecs = (1,) return (F(1),) @@ -1247,7 +1247,7 @@ def _gens_two(self): p = _l.degree() l = F(_l) - if self._hnf == O.ideal(l)._hnf: # principal ideal + if self._hnf == O.ideal(l)._hnf: # principal ideal self._gens_two_vecs = (_l,) return (l,) @@ -1255,13 +1255,13 @@ def _gens_two(self): basis = [] for row in hnf: - basis.append(sum([c1*c2 for c1,c2 in zip(row, O.basis())])) + basis.append(sum([c1 * c2 for c1, c2 in zip(row, O.basis())])) n = len(basis) alpha = None def check(alpha): - alpha_norm = alpha.norm().numerator() # denominator is 1 + alpha_norm = alpha.norm().numerator() # denominator is 1 return norm.gcd(alpha_norm // norm) == 1 # Trial 1: search for alpha among generators @@ -1273,9 +1273,9 @@ def check(alpha): # Trial 2: exhaustive search for alpha using only polynomials # with coefficients 0 or 1 for d in range(p): - G = itertools.product(itertools.product([0,1],repeat=d+1), repeat=n) + G = itertools.product(itertools.product([0, 1], repeat=d + 1), repeat=n) for g in G: - alpha = sum([R(c1)*c2 for c1,c2 in zip(g, basis)]) + alpha = sum([R(c1) * c2 for c1, c2 in zip(g, basis)]) if check(alpha): self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) return (l, alpha) @@ -1293,7 +1293,7 @@ def check(alpha): if g[j].leading_coefficient() != 1: continue - alpha = sum([c1*c2 for c1,c2 in zip(g, basis)]) + alpha = sum([c1 * c2 for c1, c2 in zip(g, basis)]) if check(alpha): self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) return (l, alpha) @@ -1383,7 +1383,7 @@ def __contains__(self, x): True """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return to_iF(x) in self._ideal def _add_(self, other): @@ -1580,7 +1580,7 @@ def gens(self): (x, y) """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return tuple(from_iF(b) for b in self._ideal.gens()) def gens_two(self): @@ -1606,7 +1606,7 @@ def gens_two(self): (x,) """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return tuple(from_iF(b) for b in self._ideal.gens_two()) def gens_over_base(self): @@ -1624,7 +1624,7 @@ def gens_over_base(self): (x, y, 1/x^2*y^2) """ F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() + iF, from_iF, to_iF = F._inversion_isomorphism() return tuple(from_iF(g) for g in self._ideal.gens_over_base()) def ideal_below(self): diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index 97f5a625819..067ca2ed090 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -547,7 +547,7 @@ def codifferent(self): + 6*x/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4)) of Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 """ - T = self._codifferent_matrix() + T = self._codifferent_matrix() return self._ideal_from_vectors(T.inverse().columns()) @cached_method @@ -686,7 +686,7 @@ def decomposition(self, ideal): m = [] for g in q.basis_matrix(): m.extend(matrix([g * mr for mr in matrices_reduced]).columns()) - beta = [c.lift() for c in matrix(m).right_kernel().basis()[0]] + beta = [c.lift() for c in matrix(m).right_kernel().basis()[0]] r = q index = 1 @@ -1291,7 +1291,7 @@ def decomposition(self, ideal): m = [] for i in range(n): m.append(sum(qgenb[j] * mtable[i][j] for j in range(n))) - beta = [fr(coeff) for coeff in matrix(m).left_kernel().basis()[0]] + beta = [fr(coeff) for coeff in matrix(m).left_kernel().basis()[0]] prime.is_prime.set_cache(True) prime._prime_below = ideal diff --git a/src/sage/rings/function_field/place_polymod.py b/src/sage/rings/function_field/place_polymod.py index e11a6c86ab7..f345c31129e 100644 --- a/src/sage/rings/function_field/place_polymod.py +++ b/src/sage/rings/function_field/place_polymod.py @@ -463,7 +463,7 @@ def _residue_field(self, name=None): prime = self.prime_ideal() # Let P be this prime ideal if self.is_infinite_place(): - _F, from_F, to_F = F._inversion_isomorphism() + _F, from_F, to_F = F._inversion_isomorphism() _prime = prime._ideal _place = _prime.place() diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 8a7f6272da5..b80a4a407ef 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -3503,10 +3503,10 @@ def quotient_char_p(I, p): if not I.is_integral(): raise ValueError("I must be an integral ideal.") - K = I.number_field() - OK = K.maximal_order() # will in the long run only really need a p-maximal order. + K = I.number_field() + OK = K.maximal_order() # will in the long run only really need a p-maximal order. M_OK = OK.free_module() - M_I = I.free_module() + M_I = I.free_module() # Now we have to quite explicitly find a way to compute # with OK / I viewed as a quotient of two F_p vector spaces, diff --git a/src/sage/rings/number_field/selmer_group.py b/src/sage/rings/number_field/selmer_group.py index 37abe43101e..87221473348 100644 --- a/src/sage/rings/number_field/selmer_group.py +++ b/src/sage/rings/number_field/selmer_group.py @@ -129,7 +129,7 @@ def _coords_in_C_p(I, C, p): """ cyclic_orders = C.gens_orders() non_p_indices = [i for i,n in enumerate(cyclic_orders) if not p.divides(n)] - p_indices = [(i, n // p) for i,n in enumerate(cyclic_orders) if p.divides(n)] + p_indices = [(i, n // p) for i,n in enumerate(cyclic_orders) if p.divides(n)] coords = C(I).exponents() if all(coords[i] == 0 for i in non_p_indices) and all(coords[i] % n == 0 for i, n in p_indices): @@ -227,7 +227,7 @@ def _root_ideal(I, C, p): """ cyclic_orders = C.gens_orders() - cyclic_gens = C.gens_ideals() + cyclic_gens = C.gens_ideals() coords = C(I).exponents() # In the next line, e=(ci/p)%n should satisfy p*e=ci (mod n): we @@ -651,7 +651,7 @@ def to_KSp(a): if debug: assert all(v % p == 0 for v in vals) one = K(1) if K == QQ else K.ideal(1) - aa = a.abs() if K == QQ else K.ideal(a) + aa = a.abs() if K == QQ else K.ideal(a) B = prod((P ** (v // p) for P, v in zip(supp,vals)), one) if debug: assert B ** p == aa diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index fa6655ca516..8f10f30add7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -405,7 +405,7 @@ def _groebner_basis_magma(self, deg_bound=None, prot=False, magma=magma_default) sage: len(gb) 5 """ - R = self.ring() + R = self.ring() if not deg_bound: mself = magma(self) else: @@ -3615,7 +3615,7 @@ def std(self): ALGORITHM: Uses Singular's ``std`` command """ - if self.side() == 'twosided': + if self.side() == 'twosided': return self.twostd() return self.ring().ideal( self.__call_singular('std'), side=self.side()) # return self.__call_singular('std') diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index a48e7e2a84a..71a01bd3b50 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -140,7 +140,7 @@ def eliminate_ll_ranked(ll_system, to_reduce, reduction_function=ll_red_nf_noredsb, reduce_ll_system=False, prot=False): - assert(ll_system) + assert ll_system from_ring = ll_system[0].ring() ll_ranks = rank(ll_system) diff --git a/src/sage/rings/polynomial/term_order.py b/src/sage/rings/polynomial/term_order.py index 687545c0c39..48e49ac89fa 100644 --- a/src/sage/rings/polynomial/term_order.py +++ b/src/sage/rings/polynomial/term_order.py @@ -802,7 +802,7 @@ def __init__(self, name='lex', n=0, force=False): singular_str = [] macaulay2_str = [] - length_pattern = re.compile(r"\(([0-9]+)\)$") # match with parenthesized block length at end + length_pattern = re.compile(r"\(([0-9]+)\)$") # match with parenthesized block length at end for block in block_names: try: block_name, block_length, _ = re.split(length_pattern,block.strip()) @@ -1312,7 +1312,7 @@ def greater_tuple_deglex(self,f,g): """ sf = sum(f.nonzero_values(sort=False)) sg = sum(g.nonzero_values(sort=False)) - return ( sf > sg or ( sf == sg and f > g )) and f or g + return ( sf > sg or ( sf == sg and f > g )) and f or g def greater_tuple_degrevlex(self,f,g): """ @@ -1396,7 +1396,7 @@ def greater_tuple_negdeglex(self,f,g): """ sf = sum(f.nonzero_values(sort=False)) sg = sum(g.nonzero_values(sort=False)) - return ( sf < sg or ( sf == sg and f > g )) and f or g + return ( sf < sg or ( sf == sg and f > g )) and f or g def greater_tuple_degneglex(self,f,g): """ @@ -1474,7 +1474,7 @@ def greater_tuple_wdeglex(self,f,g): """ sf = sum(l*r for (l,r) in zip(f,self._weights)) sg = sum(l*r for (l,r) in zip(g,self._weights)) - return (sf > sg or ( sf == sg and f > g )) and f or g + return (sf > sg or ( sf == sg and f > g )) and f or g def greater_tuple_wdegrevlex(self,f,g): """ @@ -1531,7 +1531,7 @@ def greater_tuple_negwdeglex(self,f,g): """ sf = sum(l*r for (l,r) in zip(f,self._weights)) sg = sum(l*r for (l,r) in zip(g,self._weights)) - return (sf < sg or ( sf == sg and f > g )) and f or g + return (sf < sg or ( sf == sg and f > g )) and f or g def greater_tuple_negwdegrevlex(self,f,g): """ diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index f67c7192f11..56829641624 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -422,7 +422,7 @@ def _div_(self, right): # We are computing L/R modulo the ideal. (L, R) = (self.__rep, right.__rep) - P = self.parent() + P = self.parent() I = P.defining_ideal() if not hasattr(I, 'groebner_basis'): diff --git a/src/tox.ini b/src/tox.ini index f2f1fc158fd..8e562cced8e 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -162,7 +162,7 @@ description = # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E21,E222,E225,E227,E228,E25,E271,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E21,E221,E222,E225,E227,E228,E25,E271,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E271,E301,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] From 11b2ef5ad9c9000471e5752fd6da81d0d9f072c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Feb 2024 11:36:33 +0100 Subject: [PATCH 114/191] some pep8 fixes in coding, in particular E275 --- src/sage/coding/abstract_code.py | 2 +- src/sage/coding/code_bounds.py | 2 +- src/sage/coding/cyclic_code.py | 2 +- src/sage/coding/grs_code.py | 60 +++++++-------- src/sage/coding/linear_code.py | 96 ++++++++++++------------ src/sage/coding/linear_code_no_metric.py | 2 +- src/sage/coding/punctured_code.py | 2 +- src/sage/coding/reed_muller_code.py | 12 +-- 8 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index ed84dc12517..7e58422d727 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -128,7 +128,7 @@ def _explain_constructor(cl): var = "It accepts unspecified arguments as well.\n" else: var = "" - return("{}\n{}\n{}See the documentation of {}.{} for more details." + return ("{}\n{}\n{}See the documentation of {}.{} for more details." .format(reqs, opts, var, cl.__module__, cl.__name__)) diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index b192792f11f..566add655a3 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -221,7 +221,7 @@ def _check_n_q_d(n, q, d, field_based=True): raise ValueError("The alphabet size must be an integer >1") if field_based and not is_prime_power(q): raise ValueError("The alphabet size does not make sense for a code over a field") - if not(0 < d <= n and n in ZZ and d in ZZ): + if not (0 < d <= n and n in ZZ and d in ZZ): raise ValueError("The length or minimum distance does not make sense") return True diff --git a/src/sage/coding/cyclic_code.py b/src/sage/coding/cyclic_code.py index 342b40d45c9..920ce21d3d6 100644 --- a/src/sage/coding/cyclic_code.py +++ b/src/sage/coding/cyclic_code.py @@ -577,7 +577,7 @@ def field_embedding(self): To: Finite Field in z3 of size 2^3 Defn: 1 |--> 1 """ - if not(hasattr(self, "_field_embedding")): + if not (hasattr(self, "_field_embedding")): self.defining_set() return self._field_embedding diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 82a0a5cfe72..0b077c8fbee 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -258,11 +258,11 @@ def __eq__(self, other): True """ return isinstance(other, GeneralizedReedSolomonCode) \ - and self.base_field() == other.base_field() \ - and self.length() == other.length() \ - and self.dimension() == other.dimension() \ - and self.evaluation_points() == other.evaluation_points() \ - and self.column_multipliers() == other.column_multipliers() + and self.base_field() == other.base_field() \ + and self.length() == other.length() \ + and self.dimension() == other.dimension() \ + and self.evaluation_points() == other.evaluation_points() \ + and self.column_multipliers() == other.column_multipliers() def __hash__(self): """ @@ -298,9 +298,9 @@ def _repr_(self): [40, 12, 29] Generalized Reed-Solomon Code over GF(59) """ return "[%s, %s, %s] %sReed-Solomon Code over GF(%s)"\ - % (self.length(), self.dimension(), self.minimum_distance(), - "Generalized " if self.is_generalized() else "", - self.base_field().cardinality()) + % (self.length(), self.dimension(), self.minimum_distance(), + "Generalized " if self.is_generalized() else "", + self.base_field().cardinality()) def _latex_(self): r""" @@ -319,9 +319,9 @@ def _latex_(self): [40, 12, 29] \textnormal{ Generalized Reed-Solomon Code over } \Bold{F}_{59} """ return "[%s, %s, %s] \\textnormal{ %sReed-Solomon Code over } %s"\ - % (self.length(), self.dimension(), self.minimum_distance(), - "Generalized " if self.is_generalized() else "", - self.base_field()._latex_()) + % (self.length(), self.dimension(), self.minimum_distance(), + "Generalized " if self.is_generalized() else "", + self.base_field()._latex_()) def minimum_distance(self): r""" @@ -390,7 +390,7 @@ def is_generalized(self): sage: C2.is_generalized() True """ - return not all( beta.is_one() for beta in self.column_multipliers() ) + return not all(beta.is_one() for beta in self.column_multipliers()) @cached_method def multipliers_product(self): @@ -538,7 +538,7 @@ def weight_distribution(self): q = self.base_ring().order() s = SR.var('s') wd = [1] + [0] * (d - 1) - for i in range(d, n+1): + for i in range(d, n + 1): tmp = binomial(n, i) * (q - 1) wd.append(tmp * symbolic_sum(binomial(i-1, s) * (-1)**s * q**(i - d - s), s, 0, i-d)) return wd @@ -647,9 +647,9 @@ def ReedSolomonCode(base_field, length, dimension, primitive_root=None): else: if primitive_root.multiplicative_order() != length: raise ValueError("Supplied primitive_root is not a primitive n'th root of unity") - return GeneralizedReedSolomonCode([ primitive_root**i for i in range(length) ], dimension) + return GeneralizedReedSolomonCode([primitive_root**i for i in range(length)], dimension) -####################### encoders ############################### +# ###################### encoders ############################### class GRSEvaluationVectorEncoder(Encoder): @@ -723,7 +723,7 @@ def __eq__(self, other): False """ return isinstance(other, GRSEvaluationVectorEncoder) \ - and self.code() == other.code() + and self.code() == other.code() def _repr_(self): r""" @@ -788,7 +788,7 @@ def generator_matrix(self): C = self.code() alphas = C.evaluation_points() col_mults = C.column_multipliers() - g = matrix(C.base_field(), C.dimension(), C.length(), lambda i,j: col_mults[j] * alphas[j]**i) + g = matrix(C.base_field(), C.dimension(), C.length(), lambda i, j: col_mults[j] * alphas[j]**i) g.set_immutable() return g @@ -1086,7 +1086,7 @@ def message_space(self): polynomial_ring = message_space -####################### decoders ############################### +# ###################### decoders ############################### class GRSBerlekampWelchDecoder(Decoder): @@ -1184,7 +1184,7 @@ def _latex_(self): \textnormal{ Reed-Solomon Code over } \Bold{F}_{59} """ return "\\textnormal{Berlekamp Welch decoder for }%s"\ - % self.code()._latex_() + % self.code()._latex_() def _decode_to_code_and_message(self, r): r""" @@ -1235,8 +1235,8 @@ def _decode_to_code_and_message(self, r): l0 = n-1-t l1 = n-1-t-(k-1) S = matrix(C.base_field(), n, l0+l1+2, - lambda i, j: (C.evaluation_points()[i])**j if j < (l0+1) - else r_list[i]*(C.evaluation_points()[i])**(j-(l0+1))) + lambda i, j: (C.evaluation_points()[i])**j if j < (l0+1) + else r_list[i]*(C.evaluation_points()[i])**(j-(l0+1))) S = S.right_kernel() S = S.basis_matrix().row(0) R = C.base_field()['x'] @@ -1574,7 +1574,7 @@ def _partial_xgcd(self, a, b, PolRing): r = b prev_r = a - while(r.degree() >= stop): + while (r.degree() >= stop): q = prev_r.quo_rem(r)[0] (prev_r, r) = (r, prev_r - q * r) (prev_s, s) = (s, prev_s - q * s) @@ -1858,9 +1858,9 @@ def __eq__(self, other): False """ return isinstance(other, GRSErrorErasureDecoder) \ - and self.code() == other.code() + and self.code() == other.code() - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self``. @@ -1890,7 +1890,7 @@ def _latex_(self): \textnormal{ Reed-Solomon Code over } \Bold{F}_{59} """ return "\\textnormal{Error-Erasure decoder for }%s"\ - % self.code()._latex_() + % self.code()._latex_() def decode_to_message(self, word_and_erasure_vector): r""" @@ -2095,8 +2095,8 @@ def __eq__(self, other): False """ return isinstance(other, GRSKeyEquationSyndromeDecoder) \ - and self.code() == other.code()\ - and self.input_space() == other.input_space() + and self.code() == other.code()\ + and self.input_space() == other.input_space() def _repr_(self): r""" @@ -2165,7 +2165,7 @@ def _partial_xgcd(self, a, b, PolRing): prev_r = a r = b - while(r.degree() >= t.degree()): + while (r.degree() >= t.degree()): q = prev_r.quo_rem(r)[0] prev_r, r = r, prev_r - q * r prev_t, t = t, prev_t - q * t @@ -2376,10 +2376,10 @@ def decoding_radius(self): sage: D.decoding_radius() 14 """ - return (self.code().minimum_distance()-1) // 2 + return (self.code().minimum_distance() - 1) // 2 -####################### registration ############################### +# ###################### registration ############################### GeneralizedReedSolomonCode._registered_encoders["EvaluationVector"] = GRSEvaluationVectorEncoder GeneralizedReedSolomonCode._registered_encoders["EvaluationPolynomial"] = GRSEvaluationPolynomialEncoder diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index f8f051a00d9..9d245e7d1b5 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -277,11 +277,11 @@ def _dump_code_in_leon_format(C): from sage.misc.temporary_file import tmp_filename F = C.base_ring() p = F.order() # must be prime and <11 - s = "LIBRARY code;\n"+"code=seq(%s,%s,%s,seq(\n" % (p,C.dimension(),C.length()) - Gr = [str(r)[1:-1].replace(" ","") for r in C.generator_matrix().rows()] + s = "LIBRARY code;\n" + "code=seq(%s,%s,%s,seq(\n" % (p, C.dimension(), C.length()) + Gr = [str(r)[1:-1].replace(" ", "") for r in C.generator_matrix().rows()] s += ",\n".join(Gr) + "\n));\nFINISH;" file_loc = tmp_filename() - f = open(file_loc,"w") + f = open(file_loc, "w") f.write(s) f.close() @@ -523,7 +523,7 @@ def automorphism_group_gens(self, equivalence="semilinear"): """ aut_group_can_label = self._canonize(equivalence) return aut_group_can_label.get_autom_gens(), \ - aut_group_can_label.get_autom_order() + aut_group_can_label.get_autom_order() def assmus_mattson_designs(self, t, mode=None): r""" @@ -605,29 +605,29 @@ def assmus_mattson_designs(self, t, mode=None): n = len(G.columns()) Cp = C.dual_code() wts = C.weight_distribution() - d = min([i for i in range(1,len(wts)) if wts[i] != 0]) + d = min([i for i in range(1, len(wts)) if wts[i] != 0]) if t >= d: return 0 - nonzerowts = [i for i in range(len(wts)) if wts[i] != 0 and i <= n and i >= d] + nonzerowts = [i for i in range(len(wts)) if wts[i] != 0 and d <= i <= n] if mode == "verbose": for w in nonzerowts: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w, t, n, w, wts[w] * binomial(w, t) // binomial(n, t), wts[w])) wtsp = Cp.weight_distribution() - dp = min([i for i in range(1,len(wtsp)) if wtsp[i] != 0]) + dp = min([i for i in range(1, len(wtsp)) if wtsp[i] != 0]) nonzerowtsp = [i for i in range(len(wtsp)) if wtsp[i] != 0 and i <= n-t and i >= dp] - s = len([i for i in range(1,n) if wtsp[i] != 0 and i <= n-t and i > 0]) + s = len([i for i in range(1, n) if wtsp[i] != 0 and 0 < i <= n-t]) if mode == "verbose": for w in nonzerowtsp: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w, t, n, w, wts[w] * binomial(w, t) // binomial(n, t), wts[w])) if s <= d-t: - des = [[t,(n,w,wts[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowts] - ans = ans + ["weights from C: ",nonzerowts,"designs from C: ",des] - desp = [[t,(n,w,wtsp[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowtsp] - ans = ans + ["weights from C*: ",nonzerowtsp,"designs from C*: ",desp] + des = [[t, (n, w, wts[w] * binomial(w, t) // binomial(n, t))] for w in nonzerowts] + ans = ans + ["weights from C: ", nonzerowts, "designs from C: ", des] + desp = [[t, (n, w, wtsp[w] * binomial(w, t) // binomial(n, t))] for w in nonzerowtsp] + ans = ans + ["weights from C*: ", nonzerowtsp, "designs from C*: ", desp] return ans return 0 @@ -669,15 +669,15 @@ def binomial_moment(self, i): d = self.minimum_distance() F = self.base_ring() q = F.order() - J = range(1,n+1) + J = range(1, n+1) Cp = self.dual_code() dp = Cp.minimum_distance() if i < d: return 0 - if i > n-dp and i <= n: - return binomial(n,i)*(q**(i+k-n) - 1)//(q-1) + if n - dp < i <= n: + return binomial(n, i)*(q**(i+k-n) - 1)//(q-1) from sage.combinat.set_partition import SetPartitions - P = SetPartitions(J,2).list() + P = SetPartitions(J, 2).list() b = QQ(0) for p in P: p = list(p) @@ -794,7 +794,7 @@ def canonical_representative(self, equivalence="semilinear"): """ aut_group_can_label = self._canonize(equivalence) return aut_group_can_label.get_canonical_form(), \ - aut_group_can_label.get_transporter() + aut_group_can_label.get_transporter() def characteristic(self): r""" @@ -877,7 +877,7 @@ def chinen_polynomial(self): P = RT(C.zeta_polynomial())*T**(d - dperp) if is_even(n): Pd = q**(k-n/2)*RT(Cd.zeta_polynomial()) - if not(is_even(n)): + if not (is_even(n)): Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial()) CP = P+Pd f = CP/CP(1,s) @@ -886,7 +886,7 @@ def chinen_polynomial(self): P = RT(C.zeta_polynomial()) if is_even(n): Pd = q**(k-n/2)*RT(Cd.zeta_polynomial()) - if not(is_even(n)): + if not (is_even(n)): Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial()) CP = P+Pd f = CP/CP(1,s) @@ -1660,13 +1660,13 @@ def permutation_automorphism_group(self, algorithm="partition"): 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 + Gp = A.Intersection2(Gp) # bottleneck 3 if Gp.Size() == 1: return PermutationGroup([()]) gens = Gp.GeneratorsOfGroup() stop = 1 # get ready to stop for x in gens: # if one of these gens is not an auto then don't stop - if not(self.is_permutation_automorphism(Sn_sage(x))): + if not (self.is_permutation_automorphism(Sn_sage(x))): stop = 0 break G = PermutationGroup(list(map(Sn_sage, gens))) @@ -1877,7 +1877,7 @@ def weight_distribution(self, algorithm=None): from sage.coding.binary_code import weight_dist return weight_dist(self.generator_matrix()) elif algorithm == "leon": - if not(F.order() in [2,3,5,7]): + if not (F.order() in [2,3,5,7]): raise NotImplementedError("The algorithm 'leon' is only implemented for q = 2,3,5,7.") # The GAP command DirectoriesPackageLibrary tells the location of the latest # version of the Guava libraries, so gives us the location of the Guava binaries too. @@ -2030,16 +2030,16 @@ def zeta_polynomial(self, name="T"): if d == 1 or dperp == 1: print("\n WARNING: There is no guarantee this function works when the minimum distance") print(" of the code or of the dual code equals 1.\n") - RT = PolynomialRing(QQ,"%s" % name) - R = PolynomialRing(QQ,3,"xy%s" % name) - x,y,T = R.gens() + RT = PolynomialRing(QQ, "%s" % name) + R = PolynomialRing(QQ, 3, "xy%s" % name) + x, y, T = R.gens() we = self.weight_enumerator() A = R(we) - #B = A(x+y,y,T)-(x+y)**n - B = A(x,x+y,T)-(x+y)**n + # B = A(x+y,y,T)-(x+y)**n + B = A(x, x+y, T)-(x+y)**n Bs = B.coefficients() Bs.reverse() - b = [Bs[i]/binomial(n,i+d) for i in range(len(Bs))] + b = [Bs[i]/binomial(n, i+d) for i in range(len(Bs))] r = n-d-dperp+2 P_coeffs = [] for i in range(len(b)): @@ -2397,7 +2397,7 @@ def _latex_(self): [7, 4]\textnormal{ Linear code over }\Bold{F}_{2} """ return "[%s, %s]\\textnormal{ Linear code over }%s"\ - % (self.length(), self.dimension(), self.base_ring()._latex_()) + % (self.length(), self.dimension(), self.base_ring()._latex_()) def generator_matrix(self, encoder_name=None, **kwargs): r""" @@ -2428,7 +2428,7 @@ def generator_matrix(self, encoder_name=None, **kwargs): return g -####################### encoders ############################### +# ###################### encoders ############################### class LinearCodeGeneratorMatrixEncoder(Encoder): r""" @@ -2467,7 +2467,7 @@ def __eq__(self, other): True """ return isinstance(other, LinearCodeGeneratorMatrixEncoder)\ - and self.code() == other.code() + and self.code() == other.code() def _repr_(self): r""" @@ -2519,7 +2519,7 @@ def generator_matrix(self): return g -####################### decoders ############################### +# ###################### decoders ############################### class LinearCodeSyndromeDecoder(Decoder): r""" @@ -2812,18 +2812,18 @@ def _build_lookup_table(self): F = C.base_ring() l = list(F) zero = F.zero() - #Builds a list of generators of all error positions for all - #possible error weights + # Builds a list of generators of all error positions for all + # possible error weights if zero in l: l.remove(zero) # Remember to include the no-error-vector to handle codes of minimum # distance 1 gracefully - zero_syndrome = vector(F,[F.zero()]*(n-k)) + zero_syndrome = vector(F, [F.zero()]*(n-k)) zero_syndrome.set_immutable() - lookup = {zero_syndrome: vector(F,[F.zero()]*n)} + lookup = {zero_syndrome: vector(F, [F.zero()]*n)} error_position_tables = [cartesian_product([l]*i) for i in range(1, t+1)] first_collision = True - #Filling the lookup table + # Filling the lookup table for i in range(1, t+1): stop = True patterns = Subsets(range(n), i) @@ -2839,16 +2839,16 @@ def _build_lookup_table(self): s.set_immutable() try: e_cur = lookup[s] - #if this is the first time we see a collision - #we learn the minimum distance of the code + # if this is the first time we see a collision + # we learn the minimum distance of the code if first_collision: self._code_minimum_distance = e.hamming_weight() + e_cur.hamming_weight() first_collision = False except KeyError: stop = False lookup[s] = copy(e) - #if we reached the early termination condition - #we learn the covering radius of the code + # if we reached the early termination condition + # we learn the covering radius of the code if stop: self._code_covering_radius = i - 1 self._maximum_error_weight = self._code_covering_radius @@ -3001,9 +3001,9 @@ def __eq__(self, other): True """ return isinstance(other, LinearCodeNearestNeighborDecoder)\ - and self.code() == other.code() + and self.code() == other.code() - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self``. @@ -3079,7 +3079,7 @@ def decoding_radius(self): return (self.code().minimum_distance()-1) // 2 -####################### registration ############################### +# ###################### registration ############################### LinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py index 2ae0879fa74..939e88f105c 100644 --- a/src/sage/coding/linear_code_no_metric.py +++ b/src/sage/coding/linear_code_no_metric.py @@ -892,7 +892,7 @@ def is_subcode(self, other): """ G = self.generator_matrix() for r in G.rows(): - if not(r in other): + if not (r in other): return False return True diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index 7aafa4f3f27..5272ca6c8d9 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -328,7 +328,7 @@ def structured_representation(self): C = self.original_code() pts = copy(self.punctured_positions()) list_pts = list(pts) - while(isinstance(C, PuncturedCode)): + while (isinstance(C, PuncturedCode)): cur_pts = list(C.punctured_positions()) list_len = len(list_pts) for p in cur_pts: diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 6ea8a904722..44c64ab0d94 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -258,9 +258,9 @@ def __init__(self, base_field, order, num_of_var): # input sanitization if base_field not in FiniteFields(): raise ValueError("the input `base_field` must be a FiniteField") - if not(isinstance(order, (Integer, int))): + if not (isinstance(order, (Integer, int))): raise ValueError("The order of the code must be an integer") - if not(isinstance(num_of_var, (Integer, int))): + if not (isinstance(num_of_var, (Integer, int))): raise ValueError("The number of variables must be an integer") q = base_field.cardinality() if order >= q: @@ -425,9 +425,9 @@ def __init__(self, order, num_of_var): ValueError: The order of the code must be an integer """ # input sanitization - if not(isinstance(order, (Integer, int))): + if not (isinstance(order, (Integer, int))): raise ValueError("The order of the code must be an integer") - if not(isinstance(num_of_var, (Integer, int))): + if not (isinstance(num_of_var, (Integer, int))): raise ValueError("The number of variables must be an integer") if (num_of_var < order): raise ValueError( @@ -836,7 +836,7 @@ def __eq__(self, other): False """ return isinstance(other, ReedMullerPolynomialEncoder) \ - and self.code() == other.code() + and self.code() == other.code() def encode(self, p): r""" @@ -984,7 +984,7 @@ def points(self): return ((code.base_field())**code.number_of_variables()).list() -####################### registration ############################### +# --------------- registration -------------- QAryReedMullerCode._registered_encoders["EvaluationVector"] = ReedMullerVectorEncoder QAryReedMullerCode._registered_encoders["EvaluationPolynomial"] = ReedMullerPolynomialEncoder From f014a38edeb308b4cb3014aa4bfc3074cb345c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Feb 2024 12:01:25 +0100 Subject: [PATCH 115/191] some cython-lint fixes in ecl lib --- src/sage/libs/ecl.pyx | 159 ++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 76 deletions(-) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index a4942781c5e..f2667d9ceca 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -1,18 +1,18 @@ """ Library interface to Embeddable Common Lisp (ECL) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Nils Bruin # # Distributed under the terms of the GNU General Public License (GPL) # 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/ +# **************************************************************************** -#This version of the library interface prefers to convert ECL integers and -#rationals to Sage types Integer and Rational. These parts could easily be -#adapted to work with pure Python types. +# This version of the library interface prefers to convert ECL integers and +# rationals to Sage types Integer and Rational. These parts could easily be +# adapted to work with pure Python types. from libc.stdlib cimport abort from libc.signal cimport SIGINT, SIGBUS, SIGFPE, SIGSEGV @@ -26,13 +26,13 @@ from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from cpython.object cimport Py_EQ, Py_NE -#it would be preferrable to let bint_symbolp wrap an efficient macro -#but the macro provided in object.h doesn't seem to work +# it would be preferrable to let bint_symbolp wrap an efficient macro +# but the macro provided in object.h doesn't seem to work cdef bint bint_symbolp(cl_object obj) noexcept: return not(cl_symbolp(obj) == ECL_NIL) -#these type predicates are only provided in "cl_*" form, so we wrap them -#with the proper type cast. +# these type predicates are only provided in "cl_*" form, so we wrap them +# with the proper type cast. cdef bint bint_numberp(cl_object obj) noexcept: return not(cl_numberp(obj) == ECL_NIL) @@ -77,24 +77,24 @@ cdef cl_object string_to_object(char * s) noexcept: # chained in a "free list" for quick allocation (and if the free list is empty # upon allocating a node, the array needs to be extended) -cdef cl_object insert_node_after(cl_object node,cl_object value) noexcept: - cdef cl_object next,newnode +cdef cl_object insert_node_after(cl_object node, cl_object value) noexcept: + cdef cl_object next, newnode - next=cl_cadr(node) - newnode=cl_cons(value,cl_cons(next,node)) - cl_rplaca(cl_cdr(node),newnode) + next = cl_cadr(node) + newnode = cl_cons(value, cl_cons(next, node)) + cl_rplaca(cl_cdr(node), newnode) if next != ECL_NIL: - cl_rplacd(cl_cdr(next),newnode) + cl_rplacd(cl_cdr(next), newnode) return newnode cdef void remove_node(cl_object node) noexcept: cdef cl_object next, prev - next=cl_cadr(node) - prev=cl_cddr(node) + next = cl_cadr(node) + prev = cl_cddr(node) if next != ECL_NIL: - cl_rplacd(cl_cdr(next),prev) + cl_rplacd(cl_cdr(next), prev) if prev != ECL_NIL: - cl_rplaca(cl_cdr(prev),next) + cl_rplaca(cl_cdr(prev), next) # our global list of pointers. This will be a pointer to a sentinel node, # after which all new nodes can be inserted. list_of_object gets initialised @@ -102,13 +102,15 @@ cdef void remove_node(cl_object node) noexcept: cdef cl_object list_of_objects -cdef cl_object read_from_string_clobj #our own error catching reader +cdef cl_object read_from_string_clobj # our own error catching reader cdef cl_object make_unicode_string_clobj cdef cl_object unicode_string_codepoints_clobj cdef bint ecl_has_booted = 0 -cdef char *argv = "sage" #we need a dummy argv for cl_boot (we just don't give any parameters) +cdef char *argv = "sage" +# we need a dummy argv for cl_boot (we just don't give any parameters) + # ECL signal handling @@ -135,6 +137,7 @@ def test_sigint_before_ecl_sig_on(): # We should never get here. abort() + def test_ecl_options(): """ Print an overview of the ECL options @@ -214,6 +217,7 @@ def test_ecl_options(): print('ECL_OPT_SET_GMP_MEMORY_FUNCTIONS = {0}'.format( ecl_get_option(ECL_OPT_SET_GMP_MEMORY_FUNCTIONS))) + def init_ecl(): r""" Internal function to initialize ecl. Do not call. @@ -253,23 +257,23 @@ def init_ecl(): for i in range(1, 32): sigaction(i, NULL, &sage_action[i]) - #initialize ECL + # initialize ECL ecl_set_option(ECL_OPT_SIGNAL_HANDLING_THREAD, 0) safe_cl_boot(1, &argv) - #save signal handler from ECL + # save signal handler from ECL sigaction(SIGINT, NULL, &ecl_sigint_handler) sigaction(SIGBUS, NULL, &ecl_sigbus_handler) sigaction(SIGFPE, NULL, &ecl_sigfpe_handler) sigaction(SIGSEGV, NULL, &ecl_sigsegv_handler) - #and put the Sage signal handlers back - for i in range(1,32): + # and put the Sage signal handlers back + for i in range(1, 32): sigaction(i, &sage_action[i], NULL) - #initialise list of objects and bind to global variable + # initialise list of objects and bind to global variable # *SAGE-LIST-OF-OBJECTS* to make it rooted in the reachable tree for the GC - list_of_objects=cl_cons(ECL_NIL,cl_cons(ECL_NIL,ECL_NIL)) + list_of_objects=cl_cons(ECL_NIL, cl_cons(ECL_NIL, ECL_NIL)) cl_set(string_to_object(b"*SAGE-LIST-OF-OBJECTS*"), list_of_objects) # We define our own error catching eval, apply and funcall/ @@ -277,13 +281,14 @@ def init_ecl(): # ever turn out to be a bottle neck, it should be easy to properly # compile them. - read_from_string_clobj=cl_eval(string_to_object(b"(symbol-function 'read-from-string)")) + read_from_string_clobj = cl_eval(string_to_object(b"(symbol-function 'read-from-string)")) - conditions_to_handle_clobj=ecl_list1(ecl_make_symbol(b"SERIOUS-CONDITION", b"COMMON-LISP")) - insert_node_after(list_of_objects,conditions_to_handle_clobj) + conditions_to_handle_clobj = ecl_list1(ecl_make_symbol(b"SERIOUS-CONDITION", b"COMMON-LISP")) + insert_node_after(list_of_objects, conditions_to_handle_clobj) ecl_has_booted = 1 + cdef ecl_string_to_python(cl_object s) noexcept: if bint_base_string_p(s): return char_to_str(ecl_base_string_pointer_safe(s)) @@ -342,7 +347,7 @@ cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL: cdef cl_object ret, error = NULL ecl_sig_on() - ret = safe_cl_apply(&error,func,args) + ret = safe_cl_apply(&error, func, args) ecl_sig_off() if error != NULL: @@ -357,9 +362,10 @@ cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL: cdef cl_object ecl_safe_read_string(char * s) except NULL: cdef cl_object o o = ecl_cstring_to_base_string_or_nil(s) - o = ecl_safe_funcall(read_from_string_clobj,o) + o = ecl_safe_funcall(read_from_string_clobj, o) return o + def shutdown_ecl(): r""" Shut down ecl. Do not call. @@ -375,8 +381,9 @@ def shutdown_ecl(): """ cl_shutdown() -#this prints the objects that sage wants the GC to keep track of. -#these should be all non-immediate EclObject wrapped objects + +# this prints the objects that sage wants the GC to keep track of. +# these should be all non-immediate EclObject wrapped objects def print_objects(): r""" Print GC-protection list @@ -411,6 +418,7 @@ def print_objects(): if c == ECL_NIL: break + cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: # conversion of a python object into an ecl object # most conversions are straightforward. Noteworthy are: @@ -420,9 +428,9 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: # otherwise creates a simple-string cdef bytes s - cdef cl_object L, ptr, o + cdef cl_object L, o - if isinstance(pyobj,bool): + if isinstance(pyobj, bool): if pyobj: return ECL_T else: @@ -451,7 +459,7 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: else: return o elif isinstance(pyobj, bytes): - s=pyobj + s = pyobj if read_strings: return ecl_safe_read_string(s) else: @@ -460,16 +468,16 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: - return ecl_bignum_from_mpz( (pyobj).value ) + return ecl_bignum_from_mpz((pyobj).value) elif isinstance(pyobj, Rational): return ecl_make_ratio( - python_to_ecl( (pyobj).numerator(), read_strings ), - python_to_ecl( (pyobj).denominator(), read_strings )) + python_to_ecl((pyobj).numerator(), read_strings), + python_to_ecl((pyobj).denominator(), read_strings)) elif isinstance(pyobj, EclObject): return (pyobj).obj elif isinstance(pyobj, list): L = ECL_NIL - for i in range(len(pyobj)-1,-1,-1): + for i in range(len(pyobj) - 1, -1, -1): L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) return L elif isinstance(pyobj, tuple): @@ -477,7 +485,7 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: return ECL_NIL else: L = python_to_ecl(pyobj[-1], read_strings) - for i in range(len(pyobj)-2,-1,-1): + for i in range(len(pyobj) - 2, -1, -1): L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) return L else: @@ -524,12 +532,12 @@ cdef ecl_to_python(cl_object o) noexcept: s = cl_write_to_string(1, o) return ecl_string_to_python(s) -#Maxima's BFLOAT multiprecision float type can be read with: -#def bfloat_to_python(e): -# prec=Integer(str(e.car().cddr().car())) -# mant=Integer(str(e.cdr().car())) -# exp=Integer(str(e.cddr().car())) -# return 2^(exp-prec)*mant +# Maxima's BFLOAT multiprecision float type can be read with: +# def bfloat_to_python(e): +# prec=Integer(str(e.car().cddr().car())) +# mant=Integer(str(e.cdr().car())) +# exp=Integer(str(e.cddr().car())) +# return 2^(exp-prec)*mant cdef class EclObject: r""" @@ -614,15 +622,15 @@ cdef class EclObject: EclObjects translate to themselves, so one can mix:: - sage: EclObject([1,2,EclObject([3])]) + sage: EclObject([1,2, EclObject([3])]) Calling an EclObject translates into the appropriate LISP ``apply``, where the argument is transformed into an EclObject itself, so one can flexibly apply LISP functions:: - sage: car=EclObject("car") - sage: cdr=EclObject("cdr") + sage: car = EclObject("car") + sage: cdr = EclObject("cdr") sage: car(cdr([1,2,3])) @@ -660,16 +668,16 @@ cdef class EclObject: """ - cdef cl_object obj #the wrapped object - cdef cl_object node #linked list pointer: car(node) == obj + cdef cl_object obj # the wrapped object + cdef cl_object node # linked list pointer: car(node) == obj cdef void set_obj(EclObject self, cl_object o) noexcept: if self.node: remove_node(self.node) - self.node=NULL - self.obj=o + self.node = NULL + self.obj = o if not(bint_fixnump(o) or bint_characterp(o) or bint_nullp(o)): - self.node=insert_node_after(list_of_objects,o) + self.node=insert_node_after(list_of_objects, o) def __init__(self, *args): r""" @@ -724,7 +732,7 @@ cdef class EclObject: EXAMPLES:: sage: from sage.libs.ecl import * - sage: L=EclObject([1,2,("three",'"four"')]) + sage: L = EclObject([1,2,("three",'"four"')]) sage: L.python() [1, 2, ('THREE', '"four"')] @@ -763,7 +771,7 @@ cdef class EclObject: '' """ - return "" + return "" def __str__(self): r""" @@ -827,7 +835,7 @@ cdef class EclObject: """ lispargs = EclObject(list(args)) - return ecl_wrap(ecl_safe_apply(self.obj,(lispargs).obj)) + return ecl_wrap(ecl_safe_apply(self.obj, (lispargs).obj)) def __richcmp__(left, right, int op): r""" @@ -853,20 +861,20 @@ cdef class EclObject: """ if op == Py_EQ: - if not(isinstance(left,EclObject) and isinstance(right,EclObject)): + if not(isinstance(left, EclObject) and isinstance(right, EclObject)): return False else: - return bint_equal((left).obj,(right).obj) + return bint_equal((left).obj, (right).obj) elif op == Py_NE: - if not(isinstance(left,EclObject) and isinstance(right,EclObject)): + if not(isinstance(left, EclObject) and isinstance(right, EclObject)): return True else: - return not(bint_equal((left).obj,(right).obj)) + return not(bint_equal((left).obj, (right).obj)) - #Common lisp only seems to be able to compare numeric and string types - #and does not have generic routines for doing that. - #we could dispatch based on type here, but that seems - #inappropriate for an *interface*. + # Common lisp only seems to be able to compare numeric and string types + # and does not have generic routines for doing that. + # we could dispatch based on type here, but that seems + # inappropriate for an *interface*. raise NotImplementedError("EclObjects can only be compared for equality") def __iter__(self): @@ -942,7 +950,7 @@ cdef class EclObject: raise RuntimeError("ECL runtime error") return ecl_wrap(o) - def cons(self,EclObject d): + def cons(self, EclObject d): r""" apply cons to self and argument and return the result. @@ -955,9 +963,9 @@ cdef class EclObject: """ - return ecl_wrap(cl_cons(self.obj,d.obj)) + return ecl_wrap(cl_cons(self.obj, d.obj)) - def rplaca(self,EclObject d): + def rplaca(self, EclObject d): r""" Destructively replace car(self) with d. @@ -977,8 +985,7 @@ cdef class EclObject: raise TypeError("rplaca can only be applied to a cons") cl_rplaca(self.obj, d.obj) - - def rplacd(self,EclObject d): + def rplacd(self, EclObject d): r""" Destructively replace cdr(self) with d. @@ -1340,13 +1347,13 @@ cdef class EclListIterator: self.current = ecl_wrap(ECL_NIL) return r -#input: a cl-object. Output: EclObject wrapping that. +# input: a cl-object. Output: EclObject wrapping that. cdef EclObject ecl_wrap(cl_object o) noexcept: cdef EclObject obj = EclObject.__new__(EclObject) obj.set_obj(o) return obj -#convenience routine to more easily evaluate strings +# convenience routine to more easily evaluate strings cpdef EclObject ecl_eval(str s) noexcept: r""" Read and evaluate string in Lisp and return the result @@ -1370,7 +1377,7 @@ cpdef EclObject ecl_eval(str s) noexcept: """ cdef cl_object o - o=ecl_safe_eval(python_to_ecl(s, True)) + o = ecl_safe_eval(python_to_ecl(s, True)) return ecl_wrap(o) init_ecl() From c1d0859cce76b5b171fd9b5efa27b780a39bccec Mon Sep 17 00:00:00 2001 From: Aurel Page Date: Fri, 2 Feb 2024 15:58:17 +0100 Subject: [PATCH 116/191] splitting_field: polred -> polredbest --- src/sage/rings/number_field/splitting_field.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/number_field/splitting_field.py b/src/sage/rings/number_field/splitting_field.py index bf04391a554..0bf41e50966 100644 --- a/src/sage/rings/number_field/splitting_field.py +++ b/src/sage/rings/number_field/splitting_field.py @@ -147,7 +147,7 @@ def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=No - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate - number fields using PARI's ``polred()``. This usually speeds + number fields using PARI's ``polredbest()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. @@ -549,11 +549,9 @@ def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=No # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() - M = Mpol.polred(flag=3) - n = len(M[0])-1 - Lpol = M[1][n].change_variable_name("y") - LtoM = M[0][n].change_variable_name("y").Mod(Mpol.change_variable_name("y")) - MtoL = LtoM.modreverse() + M = Mpol.polredbest(1) + Lpol = M[0].change_variable_name("y") + MtoL = M[1].lift().change_variable_name("y").Mod(Lpol) else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") From bc085f68e07c13c39a76caabe4811c9f07148b05 Mon Sep 17 00:00:00 2001 From: Aurel Page Date: Wed, 31 Jan 2024 11:25:15 +0100 Subject: [PATCH 117/191] allow algorithm=pari in LLL --- src/sage/matrix/matrix_integer_dense.pyx | 77 ++++++++++++++++++------ 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index b54ea0c2afb..d307665fc09 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -2953,10 +2953,10 @@ cdef class Matrix_integer_dense(Matrix_dense): def LLL(self, delta=None, eta=None, algorithm="fpLLL:wrapper", fp=None, prec=0, early_red=False, use_givens=False, use_siegel=False, transformation=False, **kwds): r""" - Return LLL reduced or approximated LLL reduced lattice `R` for this - matrix interpreted as a lattice. + Return LLL-reduced or approximated LLL reduced matrix `R` of the lattice + generated by the rows of self. - A lattice `(b_1, b_2, ..., b_d)` is `(\delta, \eta)`-LLL-reduced + A set of vectors `(b_1, b_2, ..., b_d)` is `(\delta, \eta)`-LLL-reduced if the two following conditions hold: - For any `i > j`, we have `\lvert \mu_{i,j} \rvert \leq \eta`. @@ -2974,23 +2974,26 @@ cdef class Matrix_integer_dense(Matrix_dense): complexity is only guaranteed for `\delta < 1`. Not every algorithm admits the case `\delta = 1`. - The lattice is returned as a matrix. Also the rank (and the - determinant) of ``self`` are cached if those are computed during - the reduction. Note that in general this only happens when - ``self.rank() == self.ncols()`` and the exact algorithm is used. + If the matrix has a nonzero kernel, the LLL-reduced matrix will contain + zero rows, so that the output has the same dimensions as the input. The + transformation matrix is always invertible over the integers. + + Also the rank (and the determinant) of ``self`` are cached if those are + computed during the reduction. Note that in general this only happens + when ``self.rank() == self.ncols()`` and the exact algorithm is used. INPUT: - ``delta`` -- (default: ``0.99``) `\delta` parameter as described - above + above, ignored by pari - ``eta`` -- (default: ``0.501``) `\eta` parameter as described above, - ignored by NTL + ignored by NTL and pari - ``algorithm`` -- string; one of the algorithms listed below (default: ``"fpLLL:wrapper"``). - - ``fp`` -- floating point number implementation: + - ``fp`` -- floating point number implementation, ignored by pari: - ``None`` -- NTL's exact reduction or fpLLL's wrapper - ``'fp'`` -- double precision: NTL's FP or fpLLL's double @@ -2999,17 +3002,17 @@ cdef class Matrix_integer_dense(Matrix_dense): - ``'xd'`` -- extended exponent: NTL's XD or fpLLL's dpe - ``'rr'`` -- arbitrary precision: NTL's RR or fpLLL's MPFR - - ``prec`` -- (default: auto choose) precision, ignored by NTL + - ``prec`` -- (default: auto choose) precision, ignored by NTL and pari - ``early_red`` -- (default: ``False``) perform early reduction, - ignored by NTL + ignored by NTL and pari - ``use_givens`` -- (default: ``False``) use Givens orthogonalization. Only applies to approximate reduction using NTL. This is slower but generally more stable. - ``use_siegel`` -- (default: ``False``) use Siegel's condition - instead of Lovász's condition, ignored by NTL + instead of Lovász's condition, ignored by NTL and pari - ``transformation`` -- (default: ``False``) also return transformation matrix. @@ -3032,6 +3035,8 @@ cdef class Matrix_integer_dense(Matrix_dense): - ``'fpLLL:wrapper'`` - fpLLL's automatic choice (default). + - ``'pari'`` - pari's qflll. + OUTPUT: A matrix over the integers. @@ -3081,14 +3086,40 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 10, 20) sage: R, U = A.LLL(transformation=True) - sage: U*A == R + sage: U * A == R True - sage: A = random_matrix(ZZ, 10, 20) sage: R, U = A.LLL(algorithm="NTL:LLL", transformation=True) - sage: U*A == R + sage: U * A == R True + sage: R, U = A.LLL(algorithm="pari", transformation=True) + sage: U * A == R + True + + Example with a nonzero kernel and rank caching:: + + sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) + sage: M.LLL()[0:2] + [0 0 0] + [0 0 0] + sage: M.rank() + 2 + + sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) + sage: M.LLL(algorithm="NTL:LLL")[0:2] + [0 0 0] + [0 0 0] + sage: M.rank() + 2 + + sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) + sage: M.LLL(algorithm="pari")[0:2] + [0 0 0] + [0 0 0] + sage: M.rank() + 2 + TESTS:: sage: matrix(ZZ, 0, 0).LLL() @@ -3234,7 +3265,7 @@ cdef class Matrix_integer_dense(Matrix_dense): R = self.new_matrix( entries=[ZZ(z) for z in A.list()]) - self.cache("rank",r) + self.cache("rank", r) elif algorithm.startswith('fpLLL:'): from fpylll import LLL, IntegerMatrix @@ -3254,6 +3285,18 @@ cdef class Matrix_integer_dense(Matrix_dense): R = A.to_matrix(self.new_matrix()) if transformation: U = U.to_matrix(self.new_matrix(U.nrows, U.ncols)) + + elif algorithm == 'pari': + # call pari with flag=4: kernel+image + # pari uses column convention: need to transpose the matrices + from sage.libs.pari import pari + A = pari(self).mattranspose() + K, T = A.qflll(4) + U = pari.matconcat([K, T]).mattranspose().sage() + R = U * self + r = ZZ(T.length()) + self.cache("rank", r) + else: raise TypeError("algorithm %s not supported"%algorithm) From d7a1ab2ccfb7937a5bf1b2cc54db3c542586cff2 Mon Sep 17 00:00:00 2001 From: Aurel Page Date: Wed, 31 Jan 2024 16:40:31 +0100 Subject: [PATCH 118/191] pari's LLL: improve doc and tests --- src/sage/matrix/matrix_integer_dense.pyx | 25 +++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index d307665fc09..ec5d9242613 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -3097,28 +3097,20 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: U * A == R True - Example with a nonzero kernel and rank caching:: + Example with a nonzero kernel:: sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) sage: M.LLL()[0:2] [0 0 0] [0 0 0] - sage: M.rank() - 2 - sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) sage: M.LLL(algorithm="NTL:LLL")[0:2] [0 0 0] [0 0 0] - sage: M.rank() - 2 - sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) sage: M.LLL(algorithm="pari")[0:2] [0 0 0] [0 0 0] - sage: M.rank() - 2 TESTS:: @@ -3150,6 +3142,17 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 0, 0) sage: R, U = A.LLL(transformation=True) + Test rank caching: + + sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) + sage: R = M.LLL(algorithm="NTL:LLL") + sage: M._cache + {'rank': 2} + sage: M._clear_cache() + sage: R = M.LLL(algorithm="pari") + sage: M._cache + {'rank': 2} + .. NOTE:: See :mod:`sage.libs.ntl.ntl_mat_ZZ.ntl_mat_ZZ.LLL` and @@ -3292,10 +3295,10 @@ cdef class Matrix_integer_dense(Matrix_dense): from sage.libs.pari import pari A = pari(self).mattranspose() K, T = A.qflll(4) - U = pari.matconcat([K, T]).mattranspose().sage() - R = U * self r = ZZ(T.length()) self.cache("rank", r) + U = pari.matconcat([K, T]).mattranspose().sage() + R = U * self else: raise TypeError("algorithm %s not supported"%algorithm) From 2a5fd15e6860a35d6c5ec719bbfb39635ea5f5d6 Mon Sep 17 00:00:00 2001 From: AurelPage Date: Wed, 31 Jan 2024 22:11:54 +0100 Subject: [PATCH 119/191] fix tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Labbé --- src/sage/matrix/matrix_integer_dense.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index ec5d9242613..39a316042eb 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -3142,7 +3142,7 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 0, 0) sage: R, U = A.LLL(transformation=True) - Test rank caching: + Test rank caching: sage: M = matrix(4,3,[1,2,3,2,4,6,7,0,1,-1,-2,-3]) sage: R = M.LLL(algorithm="NTL:LLL") From b7049bfd01c0d14109ca1536fe868dc5113c480c Mon Sep 17 00:00:00 2001 From: Aurel Page Date: Fri, 2 Feb 2024 21:14:29 +0100 Subject: [PATCH 120/191] unit group of number field: do not expand product when S is empty --- src/sage/rings/number_field/unit_group.py | 29 ++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index e2c1ebccb9e..a86d4236517 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -305,6 +305,13 @@ def __init__(self, number_field, proof=True, S=None): sage: tuple(US(K(u)) for u in US.gens()) == US.gens() True + Bug #36386 (pari stack overflow while expanding units) + sage: d = 12936642 + sage: K = QuadraticField(d) + sage: K.unit_group(proof=False) + Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 12936642 with a = 3596.754370262167? + + """ proof = get_flag(proof, "number_field") K = number_field @@ -340,20 +347,20 @@ def __init__(self, number_field, proof=True, S=None): # compute the additional S-unit generators: if S: self.__S_unit_data = pK.bnfunits(pS) + # TODO: converting the factored matrix representation of bnfunits into polynomial + # form is a *big* waste of time + su = [pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0][0:len(S)]] + su = [K(u, check=False) for u in su] else: - self.__S_unit_data = pK.bnfunits() - # TODO: converting the factored matrix representation of bnfunits into polynomial - # form is a *big* waste of time - su_fu_tu = [pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0]] - - self.__nfu = len(pK.bnf_get_fu()) # number of fundamental units - self.__nsu = len(su_fu_tu) - self.__nfu - 1 # number of S-units - self.__ntu = pK.bnf_get_tu()[0] # order of torsion + su = [] + + self.__nfu = len(fu) # number of fundamental units + self.__nsu = len(su) # number of S-units + self.__ntu = pK.bnf_get_tu()[0] # order of torsion self.__rank = self.__nfu + self.__nsu - # Move the torsion unit first, then fundamental units then S-units - gens = [K(u, check=False) for u in su_fu_tu] - gens = [gens[-1]] + gens[self.__nsu:-1] + gens[:self.__nsu] + # Put the torsion unit first, then fundamental units then S-units + gens = [K(pK.bnf_get_tu()[1], check=False)] + fu + su # Construct the abstract group: gens_orders = tuple([ZZ(self.__ntu)]+[ZZ(0)]*(self.__rank)) From d823c43780506e9bbf6313d8855b25d07607a19a Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 30 Jan 2024 13:38:36 +0100 Subject: [PATCH 121/191] flint autogen: fields of a[rc]b_poly_struct Rationale: accessing the coefficients of degree bewteen length and alloc of a polynomial is done via pol->coeffs, there is (afaik) no macro for that. And these fields are documented. --- src/sage_setup/autogen/flint/templates/types.pxd.template | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage_setup/autogen/flint/templates/types.pxd.template b/src/sage_setup/autogen/flint/templates/types.pxd.template index a60b10839ab..7794a76d5c9 100644 --- a/src/sage_setup/autogen/flint/templates/types.pxd.template +++ b/src/sage_setup/autogen/flint/templates/types.pxd.template @@ -129,7 +129,9 @@ cdef extern from "flint_wrap.h": ctypedef arb_mat_struct arb_mat_t[1] ctypedef struct arb_poly_struct: - pass + arb_ptr coeffs + long alloc + long length ctypedef arb_poly_struct[1] arb_poly_t ctypedef arb_poly_struct * arb_poly_ptr ctypedef const arb_poly_struct * arb_poly_srcptr @@ -165,7 +167,9 @@ cdef extern from "flint_wrap.h": # flint/acb_poly.h ctypedef struct acb_poly_struct: - pass + acb_ptr coeffs + long alloc + long length ctypedef acb_poly_struct[1] acb_poly_t ctypedef acb_poly_struct * acb_poly_ptr ctypedef const acb_poly_struct * acb_poly_srcptr From 956ab2c376035f7e6b7bba57a003843724f9a419 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 30 Jan 2024 13:44:03 +0100 Subject: [PATCH 122/191] flint autogen: remove two typedefs that don't exist in flint --- src/sage/libs/arb/types.pxd | 6 +----- src/sage_setup/autogen/flint/templates/types.pxd.template | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/sage/libs/arb/types.pxd b/src/sage/libs/arb/types.pxd index 7a9a2fe1706..85969f380b8 100644 --- a/src/sage/libs/arb/types.pxd +++ b/src/sage/libs/arb/types.pxd @@ -29,12 +29,8 @@ from sage.libs.flint.types cimport ( acb_mat_t, acb_poly_struct, acb_poly_t, - acb_poly_ptr, - acb_poly_srcptr, acb_calc_integrate_opt_struct, acb_calc_integrate_opt_t, acb_calc_func_t, arb_poly_struct, - arb_poly_t, - arb_poly_ptr, - arb_poly_srcptr) + arb_poly_t) diff --git a/src/sage_setup/autogen/flint/templates/types.pxd.template b/src/sage_setup/autogen/flint/templates/types.pxd.template index 7794a76d5c9..2fe18f8805e 100644 --- a/src/sage_setup/autogen/flint/templates/types.pxd.template +++ b/src/sage_setup/autogen/flint/templates/types.pxd.template @@ -133,8 +133,6 @@ cdef extern from "flint_wrap.h": long alloc long length ctypedef arb_poly_struct[1] arb_poly_t - ctypedef arb_poly_struct * arb_poly_ptr - ctypedef const arb_poly_struct * arb_poly_srcptr # flint/arb_calc.h @@ -171,8 +169,6 @@ cdef extern from "flint_wrap.h": long alloc long length ctypedef acb_poly_struct[1] acb_poly_t - ctypedef acb_poly_struct * acb_poly_ptr - ctypedef const acb_poly_struct * acb_poly_srcptr # flint/acb_calc.h ctypedef struct acb_calc_integrate_opt_struct: From 98c38719ef4e22d608898c8754e7ff8d3bad0efc Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 30 Jan 2024 13:52:54 +0100 Subject: [PATCH 123/191] move fix from #37064 to the flint interface generator --- .../autogen/flint/templates/flint_wrap.h.template | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage_setup/autogen/flint/templates/flint_wrap.h.template b/src/sage_setup/autogen/flint/templates/flint_wrap.h.template index 27237dcb2a5..58aef51a29e 100644 --- a/src/sage_setup/autogen/flint/templates/flint_wrap.h.template +++ b/src/sage_setup/autogen/flint/templates/flint_wrap.h.template @@ -40,4 +40,10 @@ #pragma pop_macro("ulong") +/* CPU_SIZE_1 and SIZE_RED_FAILURE_THRESH are defined as macros in flint/fmpz_lll.h + * and as variables in fplll/defs.h, which breaks build if linbox is compiled with fplll */ + +#undef CPU_SIZE_1 +#undef SIZE_RED_FAILURE_THRESH + #endif From c0ba507953f2c618b2fa32edaddd8b23930a38e8 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 30 Jan 2024 14:00:51 +0100 Subject: [PATCH 124/191] flint autogen: add warnings about generated files to the templates (it didn't seem worth the trouble modifying the generator itself so that it inserts the warning in the correct place...) --- .../autogen/flint/templates/flint_sage.pyx.template | 5 +++++ src/sage_setup/autogen/flint/templates/flint_wrap.h.template | 4 ++++ src/sage_setup/autogen/flint/templates/types.pxd.template | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template b/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template index 24edaaa5945..af50c1b256d 100644 --- a/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template +++ b/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template @@ -1,4 +1,9 @@ # distutils: extra_compile_args = -D_XPG6 + +# WARNING: src/sage/libs/flint/flint_sage.pyx is generated from +# src/sage_setup/autogen/flint/templates/flint_sage.pyx.template; +# please make sure that you are modifying the correct file! + """ Flint imports diff --git a/src/sage_setup/autogen/flint/templates/flint_wrap.h.template b/src/sage_setup/autogen/flint/templates/flint_wrap.h.template index 58aef51a29e..97323ede6ce 100644 --- a/src/sage_setup/autogen/flint/templates/flint_wrap.h.template +++ b/src/sage_setup/autogen/flint/templates/flint_wrap.h.template @@ -1,3 +1,7 @@ +/* WARNING: src/sage/libs/flint/flint_wrap.h is generated from + * src/sage_setup/autogen/flint/templates/flint_wrap.h.template + * please make sure that you are modifying the correct file! */ + #ifndef SAGE_FLINT_WRAP_H #define SAGE_FLINT_WRAP_H /* Using flint headers together in the same module as headers from diff --git a/src/sage_setup/autogen/flint/templates/types.pxd.template b/src/sage_setup/autogen/flint/templates/types.pxd.template index 2fe18f8805e..8d51dc29a10 100644 --- a/src/sage_setup/autogen/flint/templates/types.pxd.template +++ b/src/sage_setup/autogen/flint/templates/types.pxd.template @@ -1,5 +1,9 @@ # distutils: depends = {HEADER_LIST} +# WARNING: src/sage/libs/flint/types.pxd is generated from +# src/sage_setup/autogen/flint/templates/types.pxd.template +# please make sure that you are modifying the correct file! + """ Declarations for FLINT types """ From c179886e80fdd2fcc693fac8c30243fdb70bef02 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 30 Jan 2024 14:25:44 +0100 Subject: [PATCH 125/191] regenerate flint wrappers using flint commit 3e2c3a3e091106a25ca9c6fba28e02f2cbcd654a, which is a bit more recent than the flint version sage officially depends on but matches the API currently exposed in the generated files --- src/sage/libs/flint/flint_sage.pyx | 5 +++++ src/sage/libs/flint/flint_wrap.h | 4 ++++ src/sage/libs/flint/types.pxd | 16 ++++++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/sage/libs/flint/flint_sage.pyx b/src/sage/libs/flint/flint_sage.pyx index 7ebbc1a7571..a3c2e40a09b 100644 --- a/src/sage/libs/flint/flint_sage.pyx +++ b/src/sage/libs/flint/flint_sage.pyx @@ -1,4 +1,9 @@ # distutils: extra_compile_args = -D_XPG6 + +# WARNING: src/sage/libs/flint/flint_sage.pyx is generated from +# src/sage_setup/autogen/flint/templates/flint_sage.pyx.template; +# please make sure that you are modifying the correct file! + """ Flint imports diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index 560a1827ed2..fcfe660a1f6 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -1,3 +1,7 @@ +/* WARNING: src/sage/libs/flint/flint_wrap.h is generated from + * src/sage_setup/autogen/flint/templates/flint_wrap.h.template + * please make sure that you are modifying the correct file! */ + #ifndef SAGE_FLINT_WRAP_H #define SAGE_FLINT_WRAP_H /* Using flint headers together in the same module as headers from diff --git a/src/sage/libs/flint/types.pxd b/src/sage/libs/flint/types.pxd index 8d23a1d480d..9e227fd87b0 100644 --- a/src/sage/libs/flint/types.pxd +++ b/src/sage/libs/flint/types.pxd @@ -1,5 +1,9 @@ # distutils: depends = flint/acb.h flint/acb_calc.h flint/acb_dft.h flint/acb_dirichlet.h flint/acb_elliptic.h flint/acb_hypgeom.h flint/acb_mat.h flint/acb_modular.h flint/acb_poly.h flint/acf.h flint/aprcl.h flint/arb.h flint/arb_calc.h flint/arb_fmpz_poly.h flint/arb_fpwrap.h flint/arb_hypgeom.h flint/arb_mat.h flint/arb_poly.h flint/arf.h flint/arith.h flint/bernoulli.h flint/bool_mat.h flint/ca.h flint/ca_ext.h flint/ca_field.h flint/ca_mat.h flint/ca_poly.h flint/ca_vec.h flint/calcium.h flint/d_mat.h flint/d_vec.h flint/dirichlet.h flint/dlog.h flint/double_extras.h flint/double_interval.h flint/fexpr.h flint/fexpr_builtin.h flint/fft.h flint/flint.h flint/fmpq.h flint/fmpq_mat.h flint/fmpq_mpoly.h flint/fmpq_mpoly_factor.h flint/fmpq_poly.h flint/fmpq_vec.h flint/fmpz.h flint/fmpz_extras.h flint/fmpz_factor.h flint/fmpz_lll.h flint/fmpz_mat.h flint/fmpz_mod.h flint/fmpz_mod_mat.h flint/fmpz_mod_mpoly.h flint/fmpz_mod_mpoly_factor.h flint/fmpz_mod_poly.h flint/fmpz_mod_poly_factor.h flint/fmpz_mod_vec.h flint/fmpz_mpoly.h flint/fmpz_mpoly_factor.h flint/fmpz_mpoly_q.h flint/fmpz_poly.h flint/fmpz_poly_factor.h flint/fmpz_poly_mat.h flint/fmpz_poly_q.h flint/fmpz_vec.h flint/fmpzi.h flint/fq.h flint/fq_default.h flint/fq_default_mat.h flint/fq_default_poly.h flint/fq_default_poly_factor.h flint/fq_embed.h flint/fq_mat.h flint/fq_nmod.h flint/fq_nmod_embed.h flint/fq_nmod_mat.h flint/fq_nmod_mpoly.h flint/fq_nmod_mpoly_factor.h flint/fq_nmod_poly.h flint/fq_nmod_poly_factor.h flint/fq_nmod_vec.h flint/fq_poly.h flint/fq_poly_factor.h flint/fq_vec.h flint/fq_zech.h flint/fq_zech_embed.h flint/fq_zech_mat.h flint/fq_zech_poly.h flint/fq_zech_poly_factor.h flint/fq_zech_vec.h flint/gr.h flint/gr_generic.h flint/gr_mat.h flint/gr_mpoly.h flint/gr_poly.h flint/gr_special.h flint/gr_vec.h flint/hypgeom.h flint/long_extras.h flint/mag.h flint/mpf_mat.h flint/mpf_vec.h flint/mpfr_mat.h flint/mpfr_vec.h flint/mpn_extras.h flint/mpoly.h flint/nf.h flint/nf_elem.h flint/nmod.h flint/nmod_mat.h flint/nmod_mpoly.h flint/nmod_mpoly_factor.h flint/nmod_poly.h flint/nmod_poly_factor.h flint/nmod_poly_mat.h flint/nmod_types.h flint/nmod_vec.h flint/padic.h flint/padic_mat.h flint/padic_poly.h flint/partitions.h flint/perm.h flint/profiler.h flint/qadic.h flint/qfb.h flint/qqbar.h flint/qsieve.h flint/thread_pool.h flint/ulong_extras.h +# WARNING: src/sage/libs/flint/types.pxd is generated from +# src/sage_setup/autogen/flint/templates/types.pxd.template +# please make sure that you are modifying the correct file! + """ Declarations for FLINT types """ @@ -129,10 +133,10 @@ cdef extern from "flint_wrap.h": ctypedef arb_mat_struct arb_mat_t[1] ctypedef struct arb_poly_struct: - pass + arb_ptr coeffs + long alloc + long length ctypedef arb_poly_struct[1] arb_poly_t - ctypedef arb_poly_struct * arb_poly_ptr - ctypedef const arb_poly_struct * arb_poly_srcptr # flint/arb_calc.h @@ -165,10 +169,10 @@ cdef extern from "flint_wrap.h": # flint/acb_poly.h ctypedef struct acb_poly_struct: - pass + acb_ptr coeffs + long alloc + long length ctypedef acb_poly_struct[1] acb_poly_t - ctypedef acb_poly_struct * acb_poly_ptr - ctypedef const acb_poly_struct * acb_poly_srcptr # flint/acb_calc.h ctypedef struct acb_calc_integrate_opt_struct: From bed97d8176a726be6cbd7c1e281eb3dd55a2a9cf Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Sat, 3 Feb 2024 14:58:00 +0200 Subject: [PATCH 126/191] Small docstrings edits 1-line outputs and iterable to SetSystem --- src/sage/matroids/circuits_matroid.pyx | 62 +++++++------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index 3c211cc74d3..4eedbfcf8b8 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -92,9 +92,7 @@ cdef class CircuitsMatroid(Matroid): The groundset is the set of elements that comprise the matroid. - OUTPUT: - - a set + OUTPUT: a set EXAMPLES:: @@ -115,9 +113,7 @@ cdef class CircuitsMatroid(Matroid): - ``X`` -- an object with Python's ``frozenset`` interface - OUTPUT: - - an integer; the rank of ``X`` in the matroid + OUTPUT: an integer; the rank of ``X`` in the matroid EXAMPLES:: @@ -136,9 +132,7 @@ cdef class CircuitsMatroid(Matroid): The *rank* of the matroid is the size of the largest independent subset of the groundset. - OUTPUT: - - an integer; the rank of the matroid + OUTPUT: an integer; the rank of the matroid EXAMPLES:: @@ -157,9 +151,7 @@ cdef class CircuitsMatroid(Matroid): - ``X`` -- An object with Python's ``frozenset`` interface containing a subset of ``self.groundset()`` - OUTPUT: - - boolean + OUTPUT: boolean """ I = set(F) s = len(F) @@ -179,9 +171,7 @@ cdef class CircuitsMatroid(Matroid): - ``X`` -- An object with Python's ``frozenset`` interface containing a subset of ``self.groundset()`` - OUTPUT: - - a frozenset; a maximal independent subset of ``X`` + OUTPUT: a frozenset; a maximal independent subset of ``X`` """ I = set(F) for i in self._k_C: @@ -203,10 +193,8 @@ cdef class CircuitsMatroid(Matroid): - ``X`` -- An object with Python's ``frozenset`` interface containing a subset of ``self.groundset()``. - OUTPUT: - - a frozenset; a circuit contained in ``X``, if it exists. Otherwise an - error is raised. + OUTPUT: a frozenset; a circuit contained in ``X``, if it exists. + Otherwise an error is raised. """ I = set(F) for C in self.circuits(): @@ -223,9 +211,7 @@ cdef class CircuitsMatroid(Matroid): - ``other`` -- a matroid - ``certificate`` -- boolean (optional) - OUTPUT: - - boolean, and, if certificate = True, a dictionary giving the + OUTPUT: boolean, and, if certificate = True, a dictionary giving the isomorphism or None .. NOTE:: @@ -394,9 +380,7 @@ cdef class CircuitsMatroid(Matroid): r""" Return the bases of the matroid. - OUTPUT: - - an iterable + OUTPUT: a SetSystem EXAMPLES:: @@ -418,11 +402,7 @@ cdef class CircuitsMatroid(Matroid): def bases_iterator(self): r""" - Return the bases of the matroid. - - OUTPUT: - - an iterable + Return an iterator over the bases of the matroid. EXAMPLES:: @@ -441,11 +421,9 @@ cdef class CircuitsMatroid(Matroid): cpdef circuits(self, k=None) noexcept: """ - Return the list of circuits of the matroid. + Return the circuits of the matroid. - OUTPUT: - - a SetSystem + OUTPUT: a SetSystem """ cdef SetSystem C C = SetSystem(list(self.groundset())) @@ -461,10 +439,6 @@ cdef class CircuitsMatroid(Matroid): def circuits_iterator(self, k=None): """ Return an iterator over the circuits of the matroid. - - OUTPUT: - - an iterator """ if k: for C in self._k_C[k]: @@ -478,9 +452,7 @@ cdef class CircuitsMatroid(Matroid): """ Return the list of nonspanning circuits of the matroid. - OUTPUT: - - a SetSystem + OUTPUT: a SetSystem """ cdef SetSystem NSC NSC = SetSystem(list(self.groundset())) @@ -568,9 +540,7 @@ cdef class CircuitsMatroid(Matroid): A matroid is paving if each of its circuits has size `r` or `r+1`. - OUTPUT: - - boolean + OUTPUT: boolean EXAMPLES:: @@ -589,9 +559,7 @@ cdef class CircuitsMatroid(Matroid): For a matroid defined by its circuits, we check the circuit axioms. - OUTPUT: - - boolean + OUTPUT: boolean EXAMPLES:: From 07bb20ad5dea18fa749d57babe2f77d90f6ed1ef Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 3 Feb 2024 16:13:07 +0100 Subject: [PATCH 127/191] remove outdated part of input description in docstring --- src/sage/quadratic_forms/bqf_class_group.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/quadratic_forms/bqf_class_group.py b/src/sage/quadratic_forms/bqf_class_group.py index af9c9115185..cd226093eba 100644 --- a/src/sage/quadratic_forms/bqf_class_group.py +++ b/src/sage/quadratic_forms/bqf_class_group.py @@ -728,9 +728,6 @@ class BQFClassGroupQuotientMorphism(Morphism): is defined by finding a class representative `[a,b,c]` satisfying `f^2 \mid a` and `f \mid b` and substituting `x \mapsto x/f`. - Alternatively, one may pass the discriminants `f^2 D` and `D` instead - of the :class:`BQFClassGroup` objects `G` and `H`. - This map is a well-defined group homomorphism. EXAMPLES:: From 9e7a58576213eec31c2faa18d84d50ba1f25efa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Feb 2024 16:40:18 +0100 Subject: [PATCH 128/191] fix the linter --- src/sage_docbuild/conf.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 6b84e772dd1..f940adb0755 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -57,7 +57,7 @@ jupyter_execute_default_kernel = 'sagemath' -if os.environ.get('SAGE_LIVE_DOC', 'no') == 'yes': +if os.environ.get('SAGE_LIVE_DOC', 'no') == 'yes': SAGE_JUPYTER_SERVER = os.environ.get('SAGE_JUPYTER_SERVER', 'binder') if SAGE_JUPYTER_SERVER.startswith('binder'): # format: "binder" or @@ -197,7 +197,7 @@ def sphinx_plot(graphics, **kwds): # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # The LaTeX engine to build the docs. # https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-latex_engine @@ -205,9 +205,9 @@ def sphinx_plot(graphics, **kwds): # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of glob-style patterns that should be excluded when looking for # source files. [1] They are matched against the source file names @@ -216,11 +216,11 @@ def sphinx_plot(graphics, **kwds): exclude_patterns = ['.build'] # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. @@ -274,9 +274,9 @@ def set_intersphinx_mappings(app, config): return app.config.intersphinx_mapping = { - 'python': ('https://docs.python.org/', - os.path.join(SAGE_DOC_SRC, "common", - "python{}.inv".format(python_version))), + 'python': ('https://docs.python.org/', + os.path.join(SAGE_DOC_SRC, "common", + "python{}.inv".format(python_version))), } if PPLPY_DOCS and os.path.exists(os.path.join(PPLPY_DOCS, 'objects.inv')): app.config.intersphinx_mapping['pplpy'] = (PPLPY_DOCS, None) @@ -379,18 +379,18 @@ def set_intersphinx_mappings(app, config): html_theme_options = {} # HTML style sheet. This overrides a HTML theme's corresponding setting. -#html_style = 'default.css' +# html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. -#html_logo = 'sagelogo-word.ico' +# html_logo = 'sagelogo-word.ico' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -439,17 +439,17 @@ def set_intersphinx_mappings(app, config): # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # A list of prefixes that are ignored for sorting the Python module index ( if # this is set to ['foo.'], then foo.bar is shown under B, not F). Works only @@ -852,6 +852,7 @@ def apply(self): node.parent.insert(node.parent.index(node) + 1, cell_node) + # This replaces the setup() in sage.misc.sagedoc_conf def setup(app): app.connect('autodoc-process-docstring', process_docstring_cython) From c89b30aeaabd9c9f1d35d5b10d7ee73ed7e15bf7 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 3 Feb 2024 19:41:17 +0100 Subject: [PATCH 129/191] smarter pari conversions and factorize caching --- src/sage/matrix/matrix_integer_dense.pyx | 32 ++++++++++++++---------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 39a316042eb..2ad9c008731 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -93,9 +93,11 @@ from sage.matrix.args cimport SparseEntry, MatrixArgs_init from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack, new_gen from cypari2.paridecl cimport * +from sage.libs.pari import pari from sage.libs.pari.convert_gmp cimport INT_to_mpz from sage.libs.pari.convert_flint cimport (_new_GEN_from_fmpz_mat_t, _new_GEN_from_fmpz_mat_t_rotate90, integer_matrix) +from sage.libs.pari.convert_sage_matrix import gen_to_sage_matrix ######################################################### from sage.arith.multi_modular cimport MultiModularBasis @@ -3169,7 +3171,9 @@ cdef class Matrix_integer_dense(Matrix_dense): else: return self - U = None + r = None # rank + cdef Matrix_integer_dense R = None # LLL-reduced matrix + cdef Matrix_integer_dense U = None # transformation matrix tm = verbose("LLL of %sx%s matrix (algorithm %s)"%(self.nrows(), self.ncols(), algorithm)) import sage.libs.ntl.all @@ -3225,7 +3229,7 @@ cdef class Matrix_integer_dense(Matrix_dense): if algorithm == "NTL:LLL": if transformation: - r, det2, U = A.LLL(a,b, verbose=verb, return_U=True) + r, det2, UNTL = A.LLL(a,b, verbose=verb, return_U=True) else: r, det2 = A.LLL(a,b, verbose=verb) det2 = ZZ(det2) @@ -3258,23 +3262,23 @@ cdef class Matrix_integer_dense(Matrix_dense): raise TypeError("algorithm %s not supported"%algorithm) if isinstance(r, tuple): - r, U = r + r, UNTL = r if transformation: U = self.new_matrix(self.nrows(), self.nrows(), - entries=[ZZ(z) for z in U.list()]) + entries=[ZZ(z) for z in UNTL.list()]) r = ZZ(r) - R = self.new_matrix( + R = self.new_matrix( entries=[ZZ(z) for z in A.list()]) - self.cache("rank", r) elif algorithm.startswith('fpLLL:'): from fpylll import LLL, IntegerMatrix A = IntegerMatrix.from_matrix(self) + Ufplll = None if transformation: - U = IntegerMatrix(A.nrows, A.nrows) + Ufplll = IntegerMatrix(A.nrows, A.nrows) method = algorithm.replace("fpLLL:","") if verb: @@ -3284,26 +3288,28 @@ cdef class Matrix_integer_dense(Matrix_dense): if early_red: kwds["flags"] = kwds.get("flags", LLL.DEFAULT) | LLL.EARLY_RED - LLL.reduction(A, delta=delta, eta=eta, method=method, float_type=fp, precision=prec, U=U) + LLL.reduction(A, delta=delta, eta=eta, method=method, float_type=fp, precision=prec, U=Ufplll) R = A.to_matrix(self.new_matrix()) if transformation: - U = U.to_matrix(self.new_matrix(U.nrows, U.ncols)) + U = Ufplll.to_matrix(self.new_matrix(Ufplll.nrows, Ufplll.ncols)) elif algorithm == 'pari': # call pari with flag=4: kernel+image # pari uses column convention: need to transpose the matrices - from sage.libs.pari import pari - A = pari(self).mattranspose() + A = integer_matrix(self._matrix, 1) K, T = A.qflll(4) r = ZZ(T.length()) - self.cache("rank", r) - U = pari.matconcat([K, T]).mattranspose().sage() + # TODO: there is no optimized matrix converter pari -> sage + U = gen_to_sage_matrix(pari.matconcat([K, T]).mattranspose()) R = U * self else: raise TypeError("algorithm %s not supported"%algorithm) verbose("LLL finished", tm) + if r is not None: + self.cache("rank", r) + R.cache("rank", r) if transformation: return R, U else: From 80e120cc180b8d2a0d3eb2939430e4a6554075eb Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 4 Feb 2024 19:43:19 +0100 Subject: [PATCH 130/191] performance improvements following review --- src/sage/algebras/free_algebra.py | 16 ++++++---------- src/sage/algebras/free_algebra_element.py | 17 +++++++++-------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index be7de314ab4..2ec097d8a82 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -453,7 +453,6 @@ class FreeAlgebra_generic(CombinatorialFreeModule, Algebra): implementation of free algebras. Two corresponding free algebras in different implementations are not equal, but there is a coercion. - """ Element = FreeAlgebraElement @@ -479,14 +478,11 @@ def __init__(self, R, n, names, degrees=None): raise TypeError("argument R must be a ring") self.__ngens = n indices = FreeMonoid(n, names=names) - if degrees is None: - cat = AlgebrasWithBasis(R) - else: - if (len(degrees) == len(names) and - all(d in ZZ for d in degrees)): - cat = AlgebrasWithBasis(R).Graded() - else: + cat = AlgebrasWithBasis(R) + if degrees is not None: + if len(degrees) != len(names) or not all(d in ZZ for d in degrees): raise ValueError("argument degrees must specify an integer for each generator") + cat = cat.Graded() CombinatorialFreeModule.__init__(self, R, indices, prefix='F', category=cat) @@ -494,7 +490,7 @@ def __init__(self, R, n, names, degrees=None): if degrees is None: self._degrees = None else: - self._degrees = {g: d for g, d in zip(self.monoid().gens(), degrees)} + self._degrees = {g: ZZ(d) for g, d in zip(self.monoid().gens(), degrees)} def one_basis(self): """ @@ -819,7 +815,7 @@ def degree_on_basis(self, m): sage: (a*b*a*b^2).degree() -1 """ - return sum(self._degrees[g] * e for g, e in m) + return ZZ.sum(self._degrees[g] * e for g, e in m) def product_on_basis(self, x, y): """ diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index 8b8b23b3a8e..be318486d64 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -222,10 +222,11 @@ def is_unit(self): sage: A(2).is_unit() True """ - if self.is_zero() or len(self) > 1 or not list(self)[0][0].is_one(): + mc = self._monomial_coefficients + if not mc or len(mc) > 1: return False - c = self.leading_coefficient() - return c.is_unit() + m, c = next(iter(mc.items())) + return m.is_one() and c.is_unit() def __invert__(self): """ @@ -240,17 +241,17 @@ def __invert__(self): sage: ~A(0) Traceback (most recent call last): ... - ZeroDivisionError: element is not invertible + ValueError: element is not invertible sage: ~A(1 + x) Traceback (most recent call last): ... - ZeroDivisionError: element is not invertible + ValueError: element is not invertible """ if self.is_unit(): - c = self.leading_coefficient() - return self.parent()(~c) - raise ZeroDivisionError("element is not invertible") + m, c = next(iter(self._monomial_coefficients.items())) + return type(self)(self.parent(), {m: c.inverse_of_unit()}) + raise ValueError("element is not invertible") def _acted_upon_(self, scalar, self_on_left=False): """ From 4b44d45b8ddc7622415f77653d75732df6f5191c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 4 Feb 2024 17:06:24 +0100 Subject: [PATCH 131/191] add one method to integer-valued polynomials --- .../polynomial/integer_valued_polynomials.py | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 4ac7d585763..479edbc87ca 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -188,7 +188,7 @@ def from_polynomial(self, p): """ Convert a polynomial into the ring of integer-valued polynomials. - This raises a ``ValueError`` if this is not possible. + This raises a :class:`ValueError` if this is not possible. INPUT: @@ -1174,6 +1174,41 @@ def _poly(self, i): return binomial(x, i) class Element(CombinatorialFreeModule.Element): - pass + def variable_shift(self, k=1): + r""" + Return the image by the shift of variables. + + On polynomials, the action is the shift + on variables `x \mapsto x + k`. + + INPUT: + + - `k` -- integer (default: 1) + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).B() + sage: B = A.basis() + sage: B[5].variable_shift() + B[4] + B[5] + sage: B[5].variable_shift(-1) + -B[0] + B[1] - B[2] + B[3] - B[4] + B[5] + + TESTS:: + + sage: B[5].variable_shift(0) + B[5] + sage: B[5].variable_shift().variable_shift(-1) + B[5] + """ + if k == 0: + return self + A = self.parent() + + def on_basis(n): + return A._from_dict({A._indices(j): binomial(k, n - j) + for j in range(n + 1)}) + + return A.module_morphism(on_basis, codomain=A)(self) B = Binomial From 9a621026ae93302c89e8c7e5c26ec15c750293c7 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 4 Feb 2024 23:01:41 +0100 Subject: [PATCH 132/191] 37014: upgrade to 2024.2.1 --- build/pkgs/database_knotinfo/checksums.ini | 6 ++-- .../database_knotinfo/package-version.txt | 2 +- src/sage/knots/link.py | 29 ++++++++++--------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/build/pkgs/database_knotinfo/checksums.ini b/build/pkgs/database_knotinfo/checksums.ini index 2c43319eee6..6f174a26738 100644 --- a/build/pkgs/database_knotinfo/checksums.ini +++ b/build/pkgs/database_knotinfo/checksums.ini @@ -1,5 +1,5 @@ tarball=database_knotinfo-VERSION.tar.gz -sha1=f4b7df2f518f007d9b145bd9660c09a50c53743a -md5=62c6829c33298088a1bc0035c2b1a4b0 -cksum=401342757 +sha1=d32a5640c59c25d49ee72770aedfc6daac7c3d0e +md5=2d4104feee05547b542b1d86dd0f7675 +cksum=2620016485 upstream_url=https://pypi.io/packages/source/d/database_knotinfo/database_knotinfo-VERSION.tar.gz diff --git a/build/pkgs/database_knotinfo/package-version.txt b/build/pkgs/database_knotinfo/package-version.txt index b8af7d7d7fc..72125ba3ec8 100644 --- a/build/pkgs/database_knotinfo/package-version.txt +++ b/build/pkgs/database_knotinfo/package-version.txt @@ -1 +1 @@ -2024.1.1 +2024.2.1 diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 5e353f62e05..3f181184152 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -4187,12 +4187,13 @@ def get_knotinfo(self, mirror_version=True, unique=True): sage: import snappy # optional - snappy Plink failed to import tkinter. + sage: # optional - database_knotinfo snappy sage: from sage.knots.knotinfo import KnotInfoSeries - sage: KnotInfoSeries(10, True, True) # optional - database_knotinfo + sage: KnotInfoSeries(10, True, True) Series of knots K10 - sage: _.inject() # optional - database_knotinfo + sage: _.inject() Defining K10 - sage: for i in range(160, 166): # optional - database_knotinfo snappy + sage: for i in range(160, 166): ....: K = K10(i) ....: k = K.link(K.items.name, snappy=True) ....: print(k, '--->', k.sage_link().get_knotinfo()) @@ -4202,16 +4203,15 @@ def get_knotinfo(self, mirror_version=True, unique=True): ---> (, False) ---> (, False) ---> (, False) - - sage: snappy.Link('10_166') # optional - snappy + sage: snappy.Link('10_166') - sage: _.sage_link().get_knotinfo() # optional - database_knotinfo snappy + sage: _.sage_link().get_knotinfo() (, True) Another pair of confusion (see the corresponding `Warning `__):: - sage: # optional - snappy + sage: # optional - database_knotinfo snappy sage: Ks10_86 = snappy.Link('10_86') sage: Ks10_83 = snappy.Link('10_83') sage: Ks10_86.sage_link().get_knotinfo() @@ -4385,20 +4385,21 @@ def is_isotopic(self, other): sage: l1.is_isotopic(l3) False + sage: # optional - database_knotinfo sage: from sage.knots.knotinfo import KnotInfo - sage: L = KnotInfo.L7a7_0_0 # optional - database_knotinfo - sage: L.series(oriented=True).inject() # optional - database_knotinfo + sage: L = KnotInfo.L7a7_0_0 + sage: L.series(oriented=True).inject() Defining L7a7 - sage: L == L7a7(0) # optional - database_knotinfo + sage: L == L7a7(0) True - sage: l = L.link() # optional - database_knotinfo - sage: l.is_isotopic(L7a7(1).link()) # optional - database_knotinfo + sage: l = L.link() + sage: l.is_isotopic(L7a7(1).link()) Traceback (most recent call last): ... NotImplementedError: comparison not possible! - sage: l.is_isotopic(L7a7(2).link()) # optional - database_knotinfo + sage: l.is_isotopic(L7a7(2).link()) True - sage: l.is_isotopic(L7a7(3).link()) # optional - database_knotinfo + sage: l.is_isotopic(L7a7(3).link()) False """ from sage.misc.verbose import verbose From 5453456756cfef42634264c941c4926dc8dd5074 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Sun, 4 Feb 2024 22:09:52 +0000 Subject: [PATCH 133/191] Fix doctest with import statement --- src/sage/schemes/hyperelliptic_curves/invariants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/schemes/hyperelliptic_curves/invariants.py b/src/sage/schemes/hyperelliptic_curves/invariants.py index a0101f60a3c..258775c7bc6 100644 --- a/src/sage/schemes/hyperelliptic_curves/invariants.py +++ b/src/sage/schemes/hyperelliptic_curves/invariants.py @@ -322,6 +322,7 @@ def igusa_clebsch_invariants(f): sage: igusa_clebsch_invariants(x^5 + a*x^4 + b*x^3 + c*x^2 + d*x + e)[0] 6*b^2 - 16*a*c + 40*d + sage: from sage.schemes.hyperelliptic_curves.invariants import absolute_igusa_invariants_wamelen sage: absolute_igusa_invariants_wamelen(GF(5)['x'](x^6 - 2*x)) Traceback (most recent call last): ... From f621c7fc37151eac215ff9f94a51782b26cef37c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 5 Feb 2024 09:57:34 +0100 Subject: [PATCH 134/191] raise ArithmeticError instead of ValueError on non-invertible elements --- src/sage/algebras/free_algebra_element.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index be318486d64..b2e0df0e15b 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -241,17 +241,17 @@ def __invert__(self): sage: ~A(0) Traceback (most recent call last): ... - ValueError: element is not invertible + ArithmeticError: element is not invertible sage: ~A(1 + x) Traceback (most recent call last): ... - ValueError: element is not invertible + ArithmeticError: element is not invertible """ if self.is_unit(): m, c = next(iter(self._monomial_coefficients.items())) return type(self)(self.parent(), {m: c.inverse_of_unit()}) - raise ValueError("element is not invertible") + raise ArithmeticError("element is not invertible") def _acted_upon_(self, scalar, self_on_left=False): """ From abc6c6c131bd38e0f27db39037cb8e8b76600bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 5 Feb 2024 10:02:38 +0100 Subject: [PATCH 135/191] let's use Travis' way Co-authored-by: Travis Scrimshaw --- src/sage/rings/polynomial/integer_valued_polynomials.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 479edbc87ca..7f2d1c0fdf6 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -1206,9 +1206,12 @@ def variable_shift(self, k=1): A = self.parent() def on_basis(n): - return A._from_dict({A._indices(j): binomial(k, n - j) - for j in range(n + 1)}) + return {A._indices(j): binomial(k, n - j) for j in range(n + 1)} - return A.module_morphism(on_basis, codomain=A)(self) + from sage.data_structures.blas_dict import linear_combination + mc = self._monomial_coefficients + ret = linear_combination((on_basis((index), coeff) + for (index, coeff) in mc.items()) + return A.element_class(A, ret) B = Binomial From 3985bfc7ceb3091a0afeabb6dd13f976b6eb6d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 5 Feb 2024 10:04:29 +0100 Subject: [PATCH 136/191] fix new code --- src/sage/rings/polynomial/integer_valued_polynomials.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 7f2d1c0fdf6..dd601dd5f8d 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -1206,11 +1206,12 @@ def variable_shift(self, k=1): A = self.parent() def on_basis(n): - return {A._indices(j): binomial(k, n - j) for j in range(n + 1)} + return {A._indices(j): binomial(k, n - j) + for j in range(n + 1)} from sage.data_structures.blas_dict import linear_combination mc = self._monomial_coefficients - ret = linear_combination((on_basis((index), coeff) + ret = linear_combination((on_basis(index), coeff) for (index, coeff) in mc.items()) return A.element_class(A, ret) From c85a268791ca8220d32af2b13cbe74a368f26755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Feb 2024 18:47:43 +0100 Subject: [PATCH 137/191] make a category of Dedekind domains, remove code from ring.pyx --- src/sage/categories/all.py | 2 +- src/sage/categories/basic.py | 1 + src/sage/categories/category.py | 3 +- src/sage/categories/dedekind_domains.py | 155 ++++++++++++++++++++++++ src/sage/rings/integer_ring.pyx | 28 ++--- src/sage/rings/ring.pyx | 131 -------------------- 6 files changed, 173 insertions(+), 147 deletions(-) create mode 100644 src/sage/categories/dedekind_domains.py diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index ca2cb563f88..a344ed2a296 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -81,7 +81,7 @@ RingModules = Modules from sage.categories.vector_spaces import VectorSpaces -# (hopf) algebra structures +# (Hopf) algebra structures from sage.categories.algebras import Algebras from sage.categories.commutative_algebras import CommutativeAlgebras from sage.categories.coalgebras import Coalgebras diff --git a/src/sage/categories/basic.py b/src/sage/categories/basic.py index f3574db469d..568c9b7c114 100644 --- a/src/sage/categories/basic.py +++ b/src/sage/categories/basic.py @@ -42,6 +42,7 @@ from sage.categories.commutative_rings import CommutativeRings from sage.categories.integral_domains import IntegralDomains from sage.categories.gcd_domains import GcdDomains +from sage.categories.dedekind_domains import DedekindDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.unique_factorization_domains import UniqueFactorizationDomains diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 9164bba6a57..d7292ce72e5 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -119,7 +119,7 @@ _join_cache = WeakValueDictionary() -HALL_OF_FAME = ['Coxeter', 'Hopf', 'Weyl', 'Lie', 'Hecke'] +HALL_OF_FAME = ['Coxeter', 'Hopf', 'Weyl', 'Lie', 'Hecke', 'Dedekind'] class Category(UniqueRepresentation, SageObject): @@ -2592,6 +2592,7 @@ def category_sample(): sage: from sage.categories.category import category_sample sage: sorted(category_sample(), key=str) # needs sage.groups [Category of Coxeter groups, + Category of Dedekind domains, Category of G-sets for Symmetric group of order 8! as a permutation group, Category of Hecke modules over Rational Field, Category of Hopf algebras over Rational Field, diff --git a/src/sage/categories/dedekind_domains.py b/src/sage/categories/dedekind_domains.py new file mode 100644 index 00000000000..d02c4bb996e --- /dev/null +++ b/src/sage/categories/dedekind_domains.py @@ -0,0 +1,155 @@ +r""" +Dedekind Domains +""" +# **************************************************************************** +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# ***************************************************************************** +from sage.categories.category import Category +from sage.categories.integral_domains import IntegralDomains + + +class DedekindDomains(Category): + """ + The category of Dedekind domains. + + A Dedekind domain is a Noetherian integral domain of Krull + dimension one that is integrally closed in its field of fractions. + + EXAMPLES:: + + sage: C = DedekindDomains(); C + Category of Dedekind domains + sage: C.super_categories() + [Category of integral domains] + + TESTS:: + + sage: TestSuite(C).run() + """ + def super_categories(self): + """ + EXAMPLES:: + + sage: DedekindDomains().super_categories() + [Category of integral domains] + """ + return [IntegralDomains()] + + class ParentMethods: + def krull_dimension(self): + """ + Return 1 since Dedekind domains have Krull dimension 1. + + EXAMPLES: + + The following are examples of Dedekind domains:: + + sage: ZZ.krull_dimension() + 1 + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field + sage: OK = K.ring_of_integers() # needs sage.rings.number_field + sage: OK.krull_dimension() # needs sage.rings.number_field + 1 + + The following are not Dedekind domains but have + a ``krull_dimension`` function:: + + sage: QQ.krull_dimension() + 0 + sage: T. = PolynomialRing(QQ,2); T + Multivariate Polynomial Ring in x, y over Rational Field + sage: T.krull_dimension() + 2 + sage: U. = PolynomialRing(ZZ,3); U + Multivariate Polynomial Ring in x, y, z over Integer Ring + sage: U.krull_dimension() + 4 + + sage: # needs sage.rings.number_field + sage: K. = QuadraticField(-1) + sage: R = K.order(2*i); R + Order of conductor 2 generated by 2*i + in Number Field in i with defining polynomial x^2 + 1 with i = 1*I + sage: R.is_maximal() + False + sage: R.krull_dimension() + 1 + """ + from sage.rings.integer_ring import ZZ + return ZZ.one() + + def is_integrally_closed(self) -> bool: + """ + Return ``True`` since Dedekind domains are integrally closed. + + EXAMPLES: + + The following are examples of Dedekind domains:: + + sage: ZZ.is_integrally_closed() + True + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field + sage: OK = K.ring_of_integers() # needs sage.rings.number_field + sage: OK.is_integrally_closed() # needs sage.rings.number_field + True + + These, however, are not Dedekind domains:: + + sage: QQ.is_integrally_closed() + True + sage: S = ZZ[sqrt(5)]; S.is_integrally_closed() # needs sage.rings.number_field sage.symbolic + False + sage: T. = PolynomialRing(QQ, 2); T + Multivariate Polynomial Ring in x, y over Rational Field + sage: T.is_integral_domain() + True + """ + return True + + def integral_closure(self): + r""" + Return ``self`` since Dedekind domains are integrally closed. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') + sage: OK = K.ring_of_integers() + sage: OK.integral_closure() + Gaussian Integers generated by s in Number Field in s + with defining polynomial x^2 + 1 + sage: OK.integral_closure() == OK + True + + sage: QQ.integral_closure() == QQ + True + """ + return self + + def is_noetherian(self) -> bool: + r""" + Return ``True`` since Dedekind domains are Noetherian. + + EXAMPLES: + + The integers, `\ZZ`, and rings of integers of number + fields are Dedekind domains:: + + sage: ZZ.is_noetherian() + True + sage: x = polygen(ZZ, 'x') + sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field + sage: OK = K.ring_of_integers() # needs sage.rings.number_field + sage: OK.is_noetherian() # needs sage.rings.number_field + True + sage: QQ.is_noetherian() + True + """ + return True + + class ElementMethods: + pass diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 83d56d432c4..559de2065e2 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -56,7 +56,7 @@ import sage.rings.rational_field import sage.rings.ideal import sage.libs.pari.all import sage.rings.ideal -from sage.categories.basic import EuclideanDomains +from sage.categories.basic import EuclideanDomains, DedekindDomains from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.rings.number_field.number_field_element_base import NumberFieldElement_base from sage.structure.coerce cimport is_numpy_type @@ -122,7 +122,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain): sage: Z.is_field() False sage: Z.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: Z(2^(2^5) + 1) @@ -312,7 +313,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): True """ Parent.__init__(self, base=self, names=('x',), normalize=False, - category=(EuclideanDomains(), InfiniteEnumeratedSets().Metric())) + category=(EuclideanDomains(), DedekindDomains(), InfiniteEnumeratedSets().Metric())) self._populate_coercion_lists_(init_no_parent=True, convert_method_name='_integer_') @@ -852,17 +853,6 @@ cdef class IntegerRing_class(PrincipalIdealDomain): except TypeError: return False - def is_noetherian(self): - """ - Return ``True`` since the integers are a Noetherian ring. - - EXAMPLES:: - - sage: ZZ.is_noetherian() - True - """ - return True - def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. @@ -1132,6 +1122,11 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ Return the Krull dimension of the integers, which is 1. + .. NOTE:: + + This should rather be inherited from the category + of ``DedekindDomains``. + EXAMPLES:: sage: ZZ.krull_dimension() @@ -1143,6 +1138,11 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ Return that the integer ring is, in fact, integrally closed. + .. NOTE:: + + This should rather be inherited from the category + of ``DedekindDomains``. + EXAMPLES:: sage: ZZ.is_integrally_closed() diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 650fb387fa0..51e14e2fb39 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -1753,137 +1753,6 @@ cdef class NoetherianRing(CommutativeRing): """ return True -cdef class DedekindDomain(IntegralDomain): - """ - Generic Dedekind domain class. - - A Dedekind domain is a Noetherian integral domain of Krull - dimension one that is integrally closed in its field of fractions. - - This class is deprecated, and not actually used anywhere in the - Sage code base. If you think you need it, please create a - category :class:`DedekindDomains`, move the code of this class - there, and use it instead. - """ - def krull_dimension(self): - """ - Return 1 since Dedekind domains have Krull dimension 1. - - EXAMPLES: - - The following are examples of Dedekind domains (Noetherian integral - domains of Krull dimension one that are integrally closed over its - field of fractions):: - - sage: ZZ.krull_dimension() - 1 - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field - sage: OK = K.ring_of_integers() # needs sage.rings.number_field - sage: OK.krull_dimension() # needs sage.rings.number_field - 1 - - The following are not Dedekind domains but have - a ``krull_dimension`` function:: - - sage: QQ.krull_dimension() - 0 - sage: T. = PolynomialRing(QQ,2); T - Multivariate Polynomial Ring in x, y over Rational Field - sage: T.krull_dimension() - 2 - sage: U. = PolynomialRing(ZZ,3); U - Multivariate Polynomial Ring in x, y, z over Integer Ring - sage: U.krull_dimension() - 4 - - sage: # needs sage.rings.number_field - sage: K. = QuadraticField(-1) - sage: R = K.order(2*i); R - Order of conductor 2 generated by 2*i - in Number Field in i with defining polynomial x^2 + 1 with i = 1*I - sage: R.is_maximal() - False - sage: R.krull_dimension() - 1 - """ - return 1 - - def is_integrally_closed(self): - """ - Return ``True`` since Dedekind domains are integrally closed. - - EXAMPLES: - - The following are examples of Dedekind domains (Noetherian integral - domains of Krull dimension one that are integrally closed over its - field of fractions). - - :: - - sage: ZZ.is_integrally_closed() - True - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field - sage: OK = K.ring_of_integers() # needs sage.rings.number_field - sage: OK.is_integrally_closed() # needs sage.rings.number_field - True - - These, however, are not Dedekind domains:: - - sage: QQ.is_integrally_closed() - True - sage: S = ZZ[sqrt(5)]; S.is_integrally_closed() # needs sage.rings.number_field sage.symbolic - False - sage: T. = PolynomialRing(QQ, 2); T - Multivariate Polynomial Ring in x, y over Rational Field - sage: T.is_integral_domain() - True - """ - return True - - def integral_closure(self): - r""" - Return ``self`` since Dedekind domains are integrally closed. - - EXAMPLES:: - - sage: # needs sage.rings.number_field - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') - sage: OK = K.ring_of_integers() - sage: OK.integral_closure() - Gaussian Integers generated by s in Number Field in s - with defining polynomial x^2 + 1 - sage: OK.integral_closure() == OK - True - - sage: QQ.integral_closure() == QQ - True - """ - return self - - def is_noetherian(self): - r""" - Return ``True`` since Dedekind domains are Noetherian. - - EXAMPLES: - - The integers, `\ZZ`, and rings of integers of number - fields are Dedekind domains:: - - sage: ZZ.is_noetherian() - True - sage: x = polygen(ZZ, 'x') - sage: K = NumberField(x^2 + 1, 's') # needs sage.rings.number_field - sage: OK = K.ring_of_integers() # needs sage.rings.number_field - sage: OK.is_noetherian() # needs sage.rings.number_field - True - sage: QQ.is_noetherian() - True - """ - return True - cdef class PrincipalIdealDomain(IntegralDomain): """ From c37baf9d466f930ab512b2b911052075a8ab1d23 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 5 Feb 2024 13:17:13 +0000 Subject: [PATCH 138/191] Remove some random tags from doctests and fix reported bug --- src/sage/rings/finite_rings/hom_finite_field.pyx | 4 ++-- src/sage/rings/finite_rings/hom_finite_field_givaro.pyx | 2 +- src/sage/rings/polynomial/polynomial_quotient_ring_element.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index babac2568f4..8da5d0bdbba 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -33,7 +33,7 @@ map which is the inverse of `f` on the image of `f`:: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) # random + sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 sage: g(T) Traceback (most recent call last): @@ -130,7 +130,7 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) # random + sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 sage: g(T) diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index 75249339c6c..078c1f57a41 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -105,7 +105,7 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) sage: g = f.section() - sage: g(f(t+1)) # random + sage: g(f(t+1)) t + 1 sage: g(T) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 57947c345bf..0754ed31e7c 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -714,7 +714,7 @@ def minpoly(self): sage: (u + 1).minpoly() # needs sage.modules x^6 + 425*x^5 + 19*x^4 + 125*x^3 + 189*x^2 + 239*x + 302 sage: ext = F6.over(F2) # needs sage.modules - sage: ext(u + 1).minpoly() # indirect doctest # needs sage.modules + sage: ext(u + 1).minpoly() # indirect doctest # needs sage.modules # random x^3 + (396*i + 428)*x^2 + (80*i + 39)*x + 9*i + 178 TESTS: From 8469f2c1a6ec5900a30301b7294c2293b89f56a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 5 Feb 2024 14:09:03 +0100 Subject: [PATCH 139/191] fixing some doctests about category of ZZ --- src/doc/en/tutorial/tour_coercion.rst | 3 ++- src/doc/fr/tutorial/tour_coercion.rst | 3 ++- src/doc/ja/tutorial/tour_coercion.rst | 3 ++- src/doc/pt/tutorial/tour_coercion.rst | 4 ++-- src/sage/algebras/clifford_algebra.py | 9 ++++++--- src/sage/categories/bimodules.py | 6 ++++-- src/sage/categories/category.py | 9 ++++++--- src/sage/categories/category_types.py | 3 ++- src/sage/categories/homset.py | 10 ++++++---- src/sage/categories/modules.py | 3 ++- src/sage/categories/morphism.pyx | 2 +- src/sage/categories/primer.py | 7 +++++-- src/sage/matrix/matrix_space.py | 12 ++++++++---- src/sage/modules/free_module.py | 2 +- src/sage/modules/free_module_homspace.py | 3 ++- src/sage/rings/laurent_series_ring.py | 6 ++++-- src/sage/rings/lazy_series_ring.py | 6 ++++-- src/sage/rings/polynomial/polynomial_ring.py | 3 ++- src/sage/structure/category_object.pyx | 4 +++- src/sage/tensor/modules/finite_rank_free_module.py | 3 ++- 20 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 65b28fa0968..9a3cb8e8ee7 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -116,7 +116,8 @@ implemented in Sage as well: sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 9d666684313..41ec9264ae6 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -117,7 +117,8 @@ par ailleurs les catégories en tant que telles : sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/ja/tutorial/tour_coercion.rst b/src/doc/ja/tutorial/tour_coercion.rst index fd125a81987..6aaf7aa2911 100644 --- a/src/doc/ja/tutorial/tour_coercion.rst +++ b/src/doc/ja/tutorial/tour_coercion.rst @@ -99,7 +99,8 @@ Sageのクラス階層と圏の階層構造にはそれなりに類似が見ら sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/pt/tutorial/tour_coercion.rst b/src/doc/pt/tutorial/tour_coercion.rst index 60589243901..b5eeaa85a9f 100644 --- a/src/doc/pt/tutorial/tour_coercion.rst +++ b/src/doc/pt/tutorial/tour_coercion.rst @@ -123,10 +123,10 @@ categorias matemáticas também são implementadas no Sage: sage: Rings() Category of rings sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces - sage: ZZ.category().is_subcategory(Rings()) True sage: ZZ in Rings() diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 3b9b8ec9db6..64ba03acf87 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -416,7 +416,8 @@ def __init__(self, Q, names, category=None): sage: Cl = CliffordAlgebra(Q) sage: Cl.category() Category of finite dimensional super algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: TestSuite(Cl).run() TESTS: @@ -994,7 +995,8 @@ def lift_module_morphism(self, m, names=None): sage: phi = Cl.lift_module_morphism(m, 'abc') sage: phi.category_for() Category of finite dimensional super algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 7 -3 -7 0] [ 0 1 -1 -1 0 0 0 -17] @@ -1077,7 +1079,8 @@ def lift_isometry(self, m, names=None): sage: phi = Cl.lift_isometry(m, 'abc') sage: phi.category_for() Category of finite dimensional super algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 1 2 5 0] [ 0 1 1 2 0 0 0 5] diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py index eec2f674aa7..492a1bf9ebf 100644 --- a/src/sage/categories/bimodules.py +++ b/src/sage/categories/bimodules.py @@ -75,14 +75,16 @@ def _make_named_class_key(self, name): (Join of Category of number fields and Category of quotient fields and Category of metric spaces, - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces) sage: Bimodules(Fields(), ZZ)._make_named_class_key('element_class') (Category of fields, - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index d7292ce72e5..aa99f9841db 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -2837,7 +2837,8 @@ def _make_named_class_key(self, name): The parent class of an algebra depends only on the category of the base ring:: sage: Algebras(ZZ)._make_named_class_key("parent_class") - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces @@ -2848,7 +2849,8 @@ def _make_named_class_key(self, name): (Join of Category of number fields and Category of quotient fields and Category of metric spaces, - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces) @@ -2974,7 +2976,8 @@ def _make_named_class_key(self, name): EXAMPLES:: sage: Modules(ZZ)._make_named_class_key('element_class') - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index 4ead4f23530..7357dc5e8e4 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -225,7 +225,8 @@ def _make_named_class_key(self, name): EXAMPLES:: sage: Modules(ZZ)._make_named_class_key('element_class') - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 5bdcea35419..9925ecf45b0 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -1244,16 +1244,18 @@ def reversed(self): Set of Morphisms from Ambient free module of rank 2 over the principal ideal domain Integer Ring to Ambient free module of rank 3 over the principal ideal domain Integer Ring in - Category of finite dimensional modules with basis over (euclidean - domains and infinite enumerated sets and metric spaces) + Category of finite dimensional modules with basis over (Dedekind + domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: type(H) sage: H.reversed() Set of Morphisms from Ambient free module of rank 3 over the principal ideal domain Integer Ring to Ambient free module of rank 2 over the principal ideal domain Integer Ring in - Category of finite dimensional modules with basis over (euclidean - domains and infinite enumerated sets and metric spaces) + Category of finite dimensional modules with basis over (Dedekind + domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: type(H.reversed()) """ diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index c4f5127306f..6927e3d4936 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -884,7 +884,8 @@ def __init_extra__(self): Ambient free module of rank 3 over the principal ideal domain Integer Ring) sage: M.category() # needs sage.modules Category of Cartesian products of modules with basis - over (euclidean domains and infinite enumerated sets and metric spaces) + over (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: M.base_ring() # needs sage.modules Integer Ring diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index a30cf52e5c9..8cf6c5fe3a5 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -179,7 +179,7 @@ cdef class Morphism(Map): sage: f.category() Category of endsets of unital magmas and right modules over (euclidean domains and infinite enumerated sets and metric spaces) - and left modules over (euclidean domains + and left modules over (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) sage: # needs sage.rings.number_field diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index 0aff713bd94..178477bce7a 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -350,14 +350,17 @@ Integer Ring sage: ZZ.category() - Join of Category of euclidean domains + Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.categories() - [Join of Category of euclidean domains + [Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces, + Category of Dedekind domains, Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, Category of gcd domains, Category of integral domains, Category of domains, diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 940358b2c81..2744af3efd0 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -482,10 +482,12 @@ class MatrixSpace(UniqueRepresentation, Parent): Full MatrixSpace of 10 by 5 dense matrices over Integer Ring sage: MatrixSpace(ZZ,10,5).category() Category of infinite enumerated finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ,10,10).category() Category of infinite enumerated finite dimensional algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ,10).category() Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) @@ -649,10 +651,12 @@ def __init__(self, base_ring, nrows, ncols, sparse, implementation): Full MatrixSpace of 10 by 5 dense matrices over Integer Ring sage: MatrixSpace(ZZ,10,5).category() Category of infinite enumerated finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ,10,10).category() Category of infinite enumerated finite dimensional algebras with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ,10).category() Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index ffd6260764d..f03a36bf591 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1926,7 +1926,7 @@ class FreeModule_generic(Module_free_ambient): (finite enumerated fields and subquotients of monoids and quotients of semigroups) sage: FreeModule(ZZ,3).category() Category of finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets + (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) sage: (QQ^0).category() Category of finite enumerated finite dimensional vector spaces with basis diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 79c2b226436..d1bad17f001 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -28,7 +28,8 @@ to Ambient free module of rank 2 over the principal ideal domain Integer Ring in Category of finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: B = H.basis() sage: len(B) 6 diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index 187ac847522..cf77a8bb5a7 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -154,7 +154,8 @@ class LaurentSeriesRing(UniqueRepresentation, CommutativeRing): sage: LaurentSeriesRing(ZZ, 'x').category() Category of infinite commutative no zero divisors algebras - over (euclidean domains and infinite enumerated sets and metric spaces) + over (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: LaurentSeriesRing(QQ, 'x').category() Join of Category of complete discrete valuation fields and Category of commutative algebras over (number fields and quotient fields and metric spaces) and Category of infinite sets @@ -225,7 +226,8 @@ def __init__(self, power_series): sage: RZZ = LaurentSeriesRing(ZZ, 't') sage: RZZ.category() Category of infinite commutative no zero divisors algebras - over (euclidean domains and infinite enumerated sets and metric spaces) + over (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: TestSuite(RZZ).run() sage: R1 = LaurentSeriesRing(Zmod(1), 't') diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index ba444712df8..fb9990d0fce 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1474,7 +1474,8 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: TestSuite(L).run() sage: L.category() Category of infinite commutative no zero divisors algebras over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) sage: L = LazyLaurentSeriesRing(QQ, 't') sage: TestSuite(L).run() @@ -1488,7 +1489,8 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: L.category() Category of infinite commutative no zero divisors algebras over (unique factorization domains and commutative algebras over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) and infinite sets) sage: L = LazyLaurentSeriesRing(GF(5), 't') diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 858df388caf..80d8df4e2d2 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -244,7 +244,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: category(ZZ['x']) Join of Category of unique factorization domains and Category of commutative algebras over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) and Category of infinite sets sage: category(GF(7)['x']) Join of Category of euclidean domains diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index eba55e9f9f6..3e1eb38c73d 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -210,9 +210,11 @@ cdef class CategoryObject(SageObject): EXAMPLES:: sage: ZZ.categories() - [Join of Category of euclidean domains + [Join of Category of Dedekind domains + and Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces, + Category of Dedekind domains, Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index d8d8686eeae..23b043bf5c4 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -248,7 +248,8 @@ class :class:`~sage.modules.free_module.FreeModule_generic` Category of finite dimensional modules over Integer Ring sage: N.category() Category of finite dimensional modules with basis over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) In other words, the module created by ``FreeModule`` is actually `\ZZ^3`, while, in the absence of any distinguished basis, no *canonical* isomorphism From 3bd05abe14d4890409d17771469489d72eaf77b6 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 5 Feb 2024 16:15:03 +0100 Subject: [PATCH 140/191] avoid equality and use conversion instead --- src/sage/rings/fraction_field_element.pyx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index a43a827e240..37ff3998bfd 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -23,6 +23,7 @@ from sage.structure.element cimport FieldElement, parent from sage.structure.richcmp cimport richcmp from sage.rings.rational_field import QQ +from sage.rings.integer_ring import ZZ import sage.misc.latex as latex @@ -474,15 +475,8 @@ cdef class FractionFieldElement(FieldElement): (x1 + 2*x2 + 3*x3 + 400)/(x1 + 2*x2 + 3*x3 + 5*x5 + 6*x6 + 7*x7 + 400) """ if isinstance(in_dict, dict): - gens = self.parent().gens() - - def to_R(m): - try: - mi = gens.index(m) - except ValueError: - return m - return mi - in_dict = {to_R(m): v for m, v in in_dict.items()} + R = self.parent().base() + in_dict = {ZZ(m) if m in ZZ else R(m): v for m, v in in_dict.items()} num = self._numerator.subs(in_dict, *args, **kwds) den = self._denominator.subs(in_dict, *args, **kwds) From 981b7cca76dcab6a7c00b12cd120e8c03e494e48 Mon Sep 17 00:00:00 2001 From: Sebastian Oehms <47305845+soehms@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:05:54 +0100 Subject: [PATCH 141/191] 37014: delete whitespace Co-authored-by: Travis Scrimshaw --- src/sage/knots/knotinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index f6b7ccb3dc1..5398cd71062 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -282,7 +282,7 @@ def knotinfo_int(string): INPUT: - - ``string`` -- string that gives a value of some database entry + - ``string`` -- string that gives a value of some database entry EXAMPLES:: From 426d838cde92e3ab6e82f5b7bb3aae28fbeca1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 5 Feb 2024 21:19:49 +0100 Subject: [PATCH 142/191] fix doctest --- src/sage/categories/morphism.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 8cf6c5fe3a5..8579ca89786 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -178,7 +178,8 @@ cdef class Morphism(Map): sage: f = R.hom([t**2]) sage: f.category() Category of endsets of unital magmas and right modules over - (euclidean domains and infinite enumerated sets and metric spaces) + (Dedekind domains and euclidean domains + and infinite enumerated sets and metric spaces) and left modules over (Dedekind domains and euclidean domains and infinite enumerated sets and metric spaces) From 490ffcb3439a0becdad06f0bb78177a24599e4a2 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 5 Feb 2024 23:45:24 +0000 Subject: [PATCH 143/191] More robust doctests --- .../rings/finite_rings/hom_finite_field.pyx | 26 ++++++++++++++----- .../finite_rings/hom_finite_field_givaro.pyx | 10 +++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 8da5d0bdbba..b7bbd417273 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -24,6 +24,8 @@ Construction of an embedding:: sage: f(t) # random T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + sage: f(t) == f.im_gens()[0] + True The map `f` has a method ``section`` which returns a partially defined map which is the inverse of `f` on the image of `f`:: @@ -33,8 +35,9 @@ map which is the inverse of `f` on the image of `f`:: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: g(f(a)) == a + True sage: g(T) Traceback (most recent call last): ... @@ -130,8 +133,9 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: g(f(a)) == a + True sage: g(T) Traceback (most recent call last): @@ -207,6 +211,10 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + sage: a = k.random_element() + sage: b = k.random_element() + sage: f(a) + f(b) == f(a + b) + True sage: k. = GF(3^6) sage: K. = GF(3^9) @@ -373,8 +381,10 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: g(f(a) + f(b)) == a + b + True sage: g(T) Traceback (most recent call last): ... @@ -421,7 +431,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: embed = Frob.fixed_field()[1] - sage: hash(embed) # random + sage: hash(embed) # random -2441354824160407762 """ return Morphism.__hash__(self) @@ -760,6 +770,8 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: tfixed = kfixed.gen() sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t + sage: embed(tfixed) == embed.im_gens()[0] + True """ if self._degree_fixed == 1: k = FiniteField(self.domain().characteristic()) diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index 078c1f57a41..eebd20056e7 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -64,6 +64,10 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 Defn: t |--> 2*T^3 + 2*T^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: g(f(a) + f(b)) == g(f(a)) + g(f(b)) == a + b + True """ if not isinstance(inverse, FiniteFieldHomomorphism_givaro): raise TypeError("The given map is not an instance of FiniteFieldHomomorphism_givaro") @@ -145,6 +149,10 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 Defn: t |--> 2*T^3 + 2*T^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: f(a) + f(b) == f(a + b) + True sage: k. = GF(3^10) sage: K. = GF(3^20) @@ -184,6 +192,8 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) sage: f(t) # random 2*T^3 + 2*T^2 + 1 + sage: f(t) == f.im_gens()[0] + True """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) From 28daf53ce238b4c6c4753122a2066bb0be0b7f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Feb 2024 11:15:07 +0100 Subject: [PATCH 144/191] formal deprecation for Dedekind domains (sigh) --- src/sage/categories/fields.py | 4 +- .../modular/pollack_stevens/distributions.py | 4 +- src/sage/modules/free_module.py | 7 +-- src/sage/modules/free_quadratic_module.py | 6 +-- src/sage/rings/all.py | 4 +- src/sage/rings/complex_double.pxd | 1 - src/sage/rings/complex_double.pyx | 1 - src/sage/rings/complex_mpc.pxd | 4 +- src/sage/rings/complex_mpc.pyx | 2 +- .../rings/finite_rings/integer_mod_ring.py | 7 +-- src/sage/rings/number_field/number_field.py | 4 +- src/sage/rings/pari_ring.py | 6 +-- .../polynomial/multi_polynomial_ring_base.pxd | 4 +- .../polynomial/multi_polynomial_ring_base.pyx | 4 +- src/sage/rings/polynomial/polynomial_ring.py | 16 +++--- .../polynomial/polynomial_ring_constructor.py | 6 +-- src/sage/rings/qqbar.py | 1 - src/sage/rings/real_mpfr.pxd | 1 - src/sage/rings/ring.pyx | 51 ++++++++++++++----- 19 files changed, 79 insertions(+), 54 deletions(-) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 4f8bb141467..82769322ab9 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -118,9 +118,9 @@ def __contains__(self, x): 0 """ - import sage.rings.ring + from sage.rings.ring import _is_Field try: - return self._contains_helper(x) or sage.rings.ring._is_Field(x) + return self._contains_helper(x) or _is_Field(x) except Exception: return False diff --git a/src/sage/modular/pollack_stevens/distributions.py b/src/sage/modular/pollack_stevens/distributions.py index a83121db4fa..985d2ac5ab2 100644 --- a/src/sage/modular/pollack_stevens/distributions.py +++ b/src/sage/modular/pollack_stevens/distributions.py @@ -49,7 +49,7 @@ from sage.categories.modules import Modules from sage.structure.factory import UniqueFactory -import sage.rings.ring as ring +from sage.rings.ring import Ring lazy_import('sage.modular.pollack_stevens.dist', 'get_dist_classes') lazy_import('sage.rings.padics.factory', ['ZpCA', 'QpCR']) @@ -281,7 +281,7 @@ def __init__(self, k, p=None, prec_cap=None, base=None, character=None, ... ValueError: p must be prime """ - if not isinstance(base, ring.Ring): + if not isinstance(base, Ring): raise TypeError("base must be a ring") #from sage.rings.padics.pow_computer import PowComputer # should eventually be the PowComputer on ZpCA once that uses longs. diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index f03a36bf591..7c880f87b29 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -186,7 +186,7 @@ import sage.rings.integer import sage.rings.integer_ring import sage.rings.rational_field -import sage.rings.ring as ring +from sage.rings.ring import IntegralDomain, is_Ring from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.integral_domains import IntegralDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains @@ -213,6 +213,7 @@ ) from sage.structure.sequence import Sequence + ############################################################################### # # Constructor functions @@ -294,7 +295,7 @@ def create_object(self, version, key): and base_ring.is_maximal() and base_ring.class_number() == 1): return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) - if isinstance(base_ring, ring.IntegralDomain) or base_ring in IntegralDomains(): + if isinstance(base_ring, IntegralDomain) or base_ring in IntegralDomains(): return FreeModule_ambient_domain(base_ring, rank, sparse=sparse) return FreeModule_ambient(base_ring, rank, sparse=sparse) @@ -725,7 +726,7 @@ def span(gens, base_ring=None, check=True, already_echelonized=False): TypeError: generators must be lists of ring elements or free module elements! """ - if ring.is_Ring(gens): + if is_Ring(gens): # we allow the old input format with first input the base_ring. # Do we want to deprecate it?.. base_ring, gens = gens, base_ring diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index fa0b61e0af3..f4942502ad1 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -69,7 +69,7 @@ import sage.matrix.matrix_space import sage.misc.latex as latex -import sage.rings.ring as ring +from sage.rings.ring import Field, IntegralDomain from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.modules import free_module @@ -174,7 +174,7 @@ def FreeQuadraticModule(base_ring, rank, inner_product_matrix, M = FreeQuadraticModule_ambient_pid( base_ring, rank, sparse=sparse, inner_product_matrix=inner_product_matrix) - elif isinstance(base_ring, ring.IntegralDomain) or base_ring.is_integral_domain(): + elif isinstance(base_ring, IntegralDomain) or base_ring.is_integral_domain(): M = FreeQuadraticModule_ambient_domain( base_ring, rank, sparse=sparse, inner_product_matrix=inner_product_matrix) else: @@ -713,7 +713,7 @@ def __init__(self, base_field, dimension, degree, inner_product_matrix, sparse=F [0 0 0 0 0 1 0] [0 0 0 0 0 0 1] """ - if not isinstance(base_field, ring.Field): + if not isinstance(base_field, Field): raise TypeError("the base_field (=%s) must be a field" % base_field) free_module.FreeModule_generic_field.__init__( self, base_field=base_field, dimension=dimension, degree=degree, sparse=sparse) diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 0f83c615da7..e3c7167fe12 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -16,7 +16,9 @@ # Ring base classes from sage.rings.ring import (Ring, Field, CommutativeRing, IntegralDomain, - DedekindDomain, PrincipalIdealDomain) + PrincipalIdealDomain) + +lazy_import("sage.rings.ring", "DedekindDomain") # Ring element base classes from sage.structure.element import (CommutativeAlgebraElement, diff --git a/src/sage/rings/complex_double.pxd b/src/sage/rings/complex_double.pxd index 789db2ba3ad..f2af4bd67b7 100644 --- a/src/sage/rings/complex_double.pxd +++ b/src/sage/rings/complex_double.pxd @@ -1,7 +1,6 @@ from sage.libs.gsl.types cimport gsl_complex cimport sage.structure.element -cimport sage.rings.ring cimport sage.rings.abc diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 79c5a4cce1a..21af64dce8d 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -79,7 +79,6 @@ cdef extern from "": double cabs(double complex) import sage.rings.abc -cimport sage.rings.ring cimport sage.rings.integer from sage.structure.element cimport Element, FieldElement diff --git a/src/sage/rings/complex_mpc.pxd b/src/sage/rings/complex_mpc.pxd index 68fb73f4afa..60b3e739f83 100644 --- a/src/sage/rings/complex_mpc.pxd +++ b/src/sage/rings/complex_mpc.pxd @@ -1,7 +1,7 @@ from sage.libs.mpc.types cimport mpc_t, mpc_rnd_t cimport sage.structure.element -cimport sage.rings.ring +from sage.rings.ring cimport Field cdef class MPComplexNumber(sage.structure.element.FieldElement): cdef mpc_t value @@ -10,7 +10,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): cpdef _add_(self, other) noexcept cpdef _mul_(self, other) noexcept -cdef class MPComplexField_class(sage.rings.ring.Field): +cdef class MPComplexField_class(Field): cdef readonly int _prec cdef mpc_rnd_t __rnd cdef object __rnd_str diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index eb22622ecc4..44f25fee8c2 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -257,7 +257,7 @@ def MPComplexField(prec=53, rnd="RNDNN", names=None): return C -cdef class MPComplexField_class(sage.rings.ring.Field): +cdef class MPComplexField_class(Field): def __init__(self, int prec=53, rnd="RNDNN"): """ Initialize ``self``. diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index eb35543a20e..6e3a0c89d5d 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -65,7 +65,7 @@ from sage.arith.misc import factor from sage.arith.misc import primitive_root from sage.arith.misc import CRT_basis -import sage.rings.ring as ring +from sage.rings.ring import Field, CommutativeRing import sage.rings.abc from sage.rings.finite_rings import integer_mod import sage.rings.integer as integer @@ -85,6 +85,7 @@ class PariError(Exception): from sage.interfaces.abc import GapElement + class IntegerModFactory(UniqueFactory): r""" Return the quotient ring `\ZZ / n\ZZ`. @@ -1261,7 +1262,7 @@ def _coerce_map_from_(self, S): elif S is integer_ring.ZZ: return integer_mod.Integer_to_IntegerMod(self) elif isinstance(S, IntegerModRing_generic): - if isinstance(S, ring.Field): + if isinstance(S, Field): return None try: return integer_mod.IntegerMod_to_IntegerMod(S, self) @@ -1554,7 +1555,7 @@ def random_element(self, bound=None): True """ if bound is not None: - return ring.CommutativeRing.random_element(self, bound) + return CommutativeRing.random_element(self, bound) a = random.randint(0, self.order() - 1) return self(a) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 51b96a6c0c0..5fd638f9396 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -100,7 +100,7 @@ from sage.rings.finite_rings.integer_mod import mod from sage.categories.number_fields import NumberFields -import sage.rings.ring +from sage.rings.ring import Ring from sage.misc.latex import latex_variable_name from .unit_group import UnitGroup @@ -3536,7 +3536,7 @@ def ideal(self, *gens, **kwds): try: return self.fractional_ideal(*gens, **kwds) except ValueError: - return sage.rings.ring.Ring.ideal(self, gens, **kwds) + return Ring.ideal(self, gens, **kwds) def idealchinese(self, ideals, residues): r""" diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py index b44ee143680..e11dcf4b123 100644 --- a/src/sage/rings/pari_ring.py +++ b/src/sage/rings/pari_ring.py @@ -17,7 +17,7 @@ # http://www.gnu.org/licenses/ # **************************************************************************** import sage.libs.pari.all as pari -import sage.rings.ring as ring +from sage.rings.ring import Ring from sage.structure.element import RingElement from sage.structure.richcmp import richcmp from sage.misc.fast_methods import Singleton @@ -158,7 +158,7 @@ def __int__(self): return int(self.__x) -class PariRing(Singleton, ring.Ring): +class PariRing(Singleton, Ring): """ EXAMPLES:: @@ -170,7 +170,7 @@ class PariRing(Singleton, ring.Ring): Element = Pari def __init__(self): - ring.Ring.__init__(self, self) + Ring.__init__(self, self) def __repr__(self): return 'Pseudoring of all PARI objects.' diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd index a090a15ac34..d6350c020d3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pxd @@ -1,7 +1,7 @@ -cimport sage.rings.ring +from sage.rings.ring cimport CommutativeRing, Ring from sage.structure.parent cimport Parent -cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): +cdef class MPolynomialRing_base(CommutativeRing): cdef object _ngens cdef object _term_order cdef public object _has_singular diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index dc3bfdc2459..c102c1af41b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -32,7 +32,7 @@ def is_MPolynomialRing(x): return isinstance(x, MPolynomialRing_base) -cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): +cdef class MPolynomialRing_base(CommutativeRing): def __init__(self, base_ring, n, names, order): """ Create a polynomial ring in several variables over a commutative ring. @@ -93,7 +93,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): category = categories.rings.Rings().Finite() else: category = polynomial_default_category(base_ring.category(), n) - sage.rings.ring.Ring.__init__(self, base_ring, names, category=category) + Ring.__init__(self, base_ring, names, category=category) def is_integral_domain(self, proof=True): """ diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 80d8df4e2d2..4d71f81b313 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -148,7 +148,8 @@ import sage.categories as categories from sage.categories.morphism import IdentityMorphism -import sage.rings.ring as ring +from sage.rings.ring import (Algebra, CommutativeAlgebra, IntegralDomain, + PrincipalIdealDomain, is_Ring) from sage.structure.element import is_RingElement import sage.rings.rational_field as rational_field from sage.rings.rational_field import QQ @@ -225,7 +226,7 @@ def is_PolynomialRing(x): ######################################################################################### -class PolynomialRing_general(ring.Algebra): +class PolynomialRing_general(Algebra): """ Univariate polynomial ring over a ring. """ @@ -302,7 +303,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, self.Element = self._polynomial_class self.__cyclopoly_cache = {} self._has_singular = False - ring.Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) + Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) self._populate_coercion_lists_(convert_method_name='_polynomial_') def __reduce__(self): @@ -1708,7 +1709,7 @@ def monics( self, of_degree=None, max_degree=None ): raise ValueError("you should pass exactly one of of_degree and max_degree") -class PolynomialRing_commutative(PolynomialRing_general, ring.CommutativeAlgebra): +class PolynomialRing_commutative(PolynomialRing_general, CommutativeAlgebra): """ Univariate polynomial ring over a commutative ring. """ @@ -1835,7 +1836,7 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=True, algori class PolynomialRing_integral_domain(PolynomialRing_commutative, PolynomialRing_singular_repr, - ring.IntegralDomain): + IntegralDomain): def __init__(self, base_ring, name="x", sparse=False, implementation=None, element_class=None, category=None): """ @@ -2055,8 +2056,9 @@ def construction(self): return categories.pushout.PolynomialFunctor(self.variable_name(), sparse=self.is_sparse(), implementation=implementation), self.base_ring() + class PolynomialRing_field(PolynomialRing_integral_domain, - ring.PrincipalIdealDomain): + PrincipalIdealDomain): def __init__(self, base_ring, name="x", sparse=False, implementation=None, element_class=None, category=None): """ @@ -3594,7 +3596,7 @@ def polygen(ring_or_element, name="x"): """ if is_RingElement(ring_or_element): base_ring = ring_or_element.parent() - elif ring.is_Ring(ring_or_element): + elif is_Ring(ring_or_element): base_ring = ring_or_element else: raise TypeError("input must be a ring or ring element") diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index ba9b9453e76..42391e37f61 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -21,7 +21,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from sage.structure.category_object import normalize_names -import sage.rings.ring as ring +from sage.rings.ring import is_Ring, IntegralDomain try: import sage.rings.padics.padic_base_leaves as padic_base_leaves @@ -621,7 +621,7 @@ def PolynomialRing(base_ring, *args, **kwds): sage: R. = PolynomialRing(RIF,2) sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) """ - if not ring.is_Ring(base_ring): + if not is_Ring(base_ring): raise TypeError("base_ring {!r} must be a ring".format(base_ring)) n = -1 # Unknown number of variables @@ -860,7 +860,7 @@ def _multi_variate(base_ring, names, sparse=None, order="degrevlex", implementat if R is None and implementation == "generic": from . import multi_polynomial_ring - if isinstance(base_ring, ring.IntegralDomain): + if isinstance(base_ring, IntegralDomain): constructor = multi_polynomial_ring.MPolynomialRing_polydict_domain else: constructor = multi_polynomial_ring.MPolynomialRing_polydict diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 9c286af01ec..4d92d64d80f 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -560,7 +560,6 @@ import sage.rings.abc import sage.rings.number_field.number_field_base -import sage.rings.ring from sage.arith.misc import factor from sage.categories.action import Action from sage.misc.cachefunc import cached_method diff --git a/src/sage/rings/real_mpfr.pxd b/src/sage/rings/real_mpfr.pxd index f89ec9557fd..6e06e5c5ea4 100644 --- a/src/sage/rings/real_mpfr.pxd +++ b/src/sage/rings/real_mpfr.pxd @@ -1,6 +1,5 @@ from sage.libs.mpfr.types cimport mpfr_rnd_t, mpfr_t, mpfr_prec_t -cimport sage.rings.ring cimport sage.rings.abc cimport sage.structure.element diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 51e14e2fb39..2356eb44981 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -7,25 +7,25 @@ specific base classes. .. WARNING:: - Those classes, except maybe for the lowest ones like :class:`Ring`, - :class:`CommutativeRing`, :class:`Algebra` and :class:`CommutativeAlgebra`, + Those classes, except maybe for the lowest ones like + :class:`CommutativeRing` and :class:`CommutativeAlgebra`, are being progressively deprecated in favor of the corresponding categories. which are more flexible, in particular with respect to multiple inheritance. The class inheritance hierarchy is: -- :class:`Ring` +- :class:`Ring` (to be deprecated) - - :class:`Algebra` + - :class:`Algebra` (to be deprecated) - :class:`CommutativeRing` - - :class:`NoetherianRing` - - :class:`CommutativeAlgebra` - - :class:`IntegralDomain` + - :class:`NoetherianRing` (deprecated) + - :class:`CommutativeAlgebra` (to be deprecated) + - :class:`IntegralDomain` (deprecated) - - :class:`DedekindDomain` - - :class:`PrincipalIdealDomain` + - :class:`DedekindDomain` (deprecated and essentially removed) + - :class:`PrincipalIdealDomain` (deprecated) Subclasses of :class:`PrincipalIdealDomain` are @@ -43,7 +43,7 @@ are Noetherian PIDs. advance* whether or not a ring belongs in one of these classes; e.g. some orders in number fields are Dedekind domains, but others are not, and we still want to offer a unified interface, so orders are never instances of the -:class:`DedekindDomain` class.) +deprecated :class:`DedekindDomain` class.) AUTHORS: @@ -54,18 +54,32 @@ AUTHORS: - Simon King (2011-05-20): Modify multiplication and _ideal_class_ to support ideals of non-commutative rings. +TESTS: + +This is to test a deprecation:: + + sage: from sage.rings.ring import DedekindDomain + sage: class No(DedekindDomain): + ....: pass + sage: F = No(QQ) + ...: + DeprecationWarning: use the category DedekindDomains + See https://github.com/sagemath/sage/issues/37234 for details. + sage: F.category() + Category of Dedekind domains """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005, 2007 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # 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 sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecation from sage.structure.coerce cimport coercion_model from sage.structure.parent cimport Parent @@ -75,6 +89,7 @@ from sage.misc.prandom import randint from sage.categories.rings import Rings from sage.categories.commutative_rings import CommutativeRings from sage.categories.integral_domains import IntegralDomains +from sage.categories.dedekind_domains import DedekindDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains _Rings = Rings() @@ -1608,7 +1623,7 @@ cdef class IntegralDomain(CommutativeRing): This method is used by all the abstract subclasses of :class:`IntegralDomain`, like :class:`NoetherianRing`, - :class:`PrincipalIdealDomain`, :class:`DedekindDomain`, + :class:`PrincipalIdealDomain`, :class:`Field`, ... in order to avoid cascade calls Field.__init__ -> PrincipalIdealDomain.__init__ -> IntegralDomain.__init__ -> @@ -1754,6 +1769,14 @@ cdef class NoetherianRing(CommutativeRing): return True +cdef class DedekindDomain(IntegralDomain): + _default_category = DedekindDomains() + + def __init__(self, *args, **kwds): + deprecation(37234, "use the category DedekindDomains") + super().__init__(*args, **kwds) + + cdef class PrincipalIdealDomain(IntegralDomain): """ Generic principal ideal domain. From 0a56fbf50f0493f183f5b0276a051d44e518c9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Feb 2024 11:54:05 +0100 Subject: [PATCH 145/191] a few details in combinat folder --- .../cluster_algebra_quiver/mutation_type.py | 2 +- src/sage/combinat/designs/bibd.py | 2 +- src/sage/combinat/designs/database.py | 4 ++-- .../combinat/designs/difference_family.py | 5 ++-- src/sage/combinat/designs/latin_squares.py | 2 +- .../combinat/designs/orthogonal_arrays.py | 8 +++---- src/sage/combinat/designs/resolvable_bibd.py | 2 +- .../designs/steiner_quadruple_systems.py | 2 +- src/sage/combinat/finite_state_machine.py | 23 ++++++++++--------- src/sage/combinat/partition.py | 11 +++++---- src/sage/combinat/partition_algebra.py | 2 +- .../combinat/partition_shifting_algebras.py | 2 +- .../combinat/words/word_infinite_datatypes.py | 2 +- 13 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 32f9ccc52d7..e49bc3d25e1 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -1045,7 +1045,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): # if one edge appearing in exactly two oriented triangles, test that it is not a double-edge and then # test that either the third or fourth vertices (from the oriented triangles) is of degree 2. # Then initializes the long_cycle as this triangle including the degree 2 vertex, as long as no other long_cycles. - elif count == 1 and not dg.has_multiple_edges() and not multiple_trian_edges[0] in dg.multiple_edges(): + elif count == 1 and not dg.has_multiple_edges() and multiple_trian_edges[0] not in dg.multiple_edges(): multiple_trian_edge = multiple_trian_edges[0] neighbors = list(set(dg.neighbors( multiple_trian_edge[0] )).intersection(dg.neighbors( multiple_trian_edge[1] ))) if dg.degree( neighbors[0] ) == 2: diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 7363fa05f90..695e31ab70e 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -879,7 +879,7 @@ def BIBD_from_PBD(PBD, v, k, check=True, base_cases=None): for X in PBD: n = len(X) N = (k-1)*n+1 - if not (n,k) in base_cases: + if (n,k) not in base_cases: base_cases[n,k] = _relabel_bibd(balanced_incomplete_block_design(N,k), N) for XX in base_cases[n,k]: diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 5eb2b90802a..f3e1ef73350 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -2469,7 +2469,7 @@ def QDM_57_9_1_1_8(): ((45,7,1,1,9), QDM_45_7_1_1_9), ((54,7,1,1,8), QDM_54_7_1_1_8), ((57,9,1,1,8), QDM_57_9_1_1_8)]: - if not (n+u,lmbda) in QDM: + if (n+u,lmbda) not in QDM: QDM[n+u,lmbda] = {} QDM[n+u,lmbda][n,lmbda,mu,u] = (k,f) @@ -2701,7 +2701,7 @@ def QDM_57_9_1_1_8(): # Translate all V(m,t) into (mt+1,m+2;1,0;t)-QDM constructors for (m,t),(vec,source) in Vmt_vectors.items(): n,k,lmbda,mu,u = (m*t+1,m+2,1,0,t) - if not (n+u,lmbda) in QDM: + if (n+u,lmbda) not in QDM: QDM[n+u,lmbda] = {} QDM[n+u,lmbda][n,lmbda,mu,u] = (k,lambda m=m,t=t,vec=vec:QDM_from_Vmt(m,t,vec)) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 393ea4b9b47..357a94fbb44 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -3138,8 +3138,9 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch See also :wikipedia:`Difference_set`. - If there is no such difference family, an ``EmptySetError`` is raised and if - there is no construction at the moment ``NotImplementedError`` is raised. + If there is no such difference family, an ``EmptySetError`` is raised and + if there is no construction at the moment :class:`NotImplementedError` + is raised. INPUT: diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 27452495862..58662fbeb88 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -299,7 +299,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): Unknown If you ask for such a MOLS then you will respectively get an informative - ``EmptySetError`` or ``NotImplementedError``:: + ``EmptySetError`` or :class:`NotImplementedError`:: sage: designs.mutually_orthogonal_latin_squares(5, 5) Traceback (most recent call last): diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 312f6a4b375..b4a14b0e7b6 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -190,7 +190,7 @@ def transversal_design(k, n, resolvable=False, check=True, existence=False): Unknown If you ask for a transversal design that Sage is not able to build then an - ``EmptySetError`` or a ``NotImplementedError`` is raised:: + ``EmptySetError`` or a :class:`NotImplementedError` is raised:: sage: designs.transversal_design(47, 100) Traceback (most recent call last): @@ -1359,7 +1359,7 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): return True independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) - elif max_hole == 1 and not orthogonal_array(k,n,existence=True) is True: + elif max_hole == 1 and orthogonal_array(k,n,existence=True) is not True: return orthogonal_array(k,n,existence=existence) # From a quasi-difference matrix @@ -2117,8 +2117,8 @@ class OAMainFunctions: 6 If you ask for an orthogonal array that does not exist, then you will - either obtain an ``EmptySetError`` (if it knows that such an orthogonal array - does not exist) or a ``NotImplementedError``:: + either obtain an ``EmptySetError`` (if it knows that such an orthogonal + array does not exist) or a :class:`NotImplementedError`:: sage: designs.orthogonal_arrays.build(4,2) Traceback (most recent call last): diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 347a6a4d27b..e79e996dcb5 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -738,7 +738,7 @@ def PBD_4_7_from_Y(gdd,check=True): "but there are other: {}".format(txt)) for gs in group_sizes: - if not PBD_4_7(3*gs+1,existence=True) is True: + if PBD_4_7(3*gs+1,existence=True) is not True: raise RuntimeError("A group has size {} but I do not know how to " "build a ({},[4,7])-PBD".format(gs,3*gs+1)) diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index 8312f2f92c8..7c7819c4138 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -709,7 +709,7 @@ def steiner_quadruple_system(n, check=False): ....: sqs = designs.steiner_quadruple_system(n, check=True) """ n = int(n) - if not ((n % 6) in [2, 4]): + if (n % 6) not in [2, 4]: raise ValueError("n mod 6 must be equal to 2 or 4") elif n == 4: sqs = IncidenceStructure(4, [[0,1,2,3]], copy=False, check=False) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 057eee403dc..13e523df4e6 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -591,8 +591,7 @@ ... ValueError: Invalid input sequence. -The raised ``ValueError`` -means `13` is not divisible by `3`. +The raised :class:`ValueError` means `13` is not divisible by `3`. .. _finite_state_machine_gray_code_example: @@ -2120,7 +2119,7 @@ def _epsilon_cycle_output_empty_(self, fsm=None): ``True`` or ``False`` - A ``ValueError`` is raised when ``self`` is not in an epsilon + A :class:`ValueError` is raised when ``self`` is not in an epsilon cycle. TESTS:: @@ -2537,7 +2536,9 @@ def duplicate_transition_ignore(old_transition, new_transition): def duplicate_transition_raise_error(old_transition, new_transition): """ Alternative function for handling duplicate transitions in finite - state machines. This implementation raises a ``ValueError``. + state machines. + + This implementation raises a :class:`ValueError`. See the documentation of the ``on_duplicate_transition`` parameter of :class:`FiniteStateMachine`. @@ -2551,7 +2552,7 @@ def duplicate_transition_raise_error(old_transition, new_transition): OUTPUT: - Nothing. A ``ValueError`` is raised. + Nothing. A :class:`ValueError` is raised. EXAMPLES:: @@ -2812,7 +2813,7 @@ class FiniteStateMachine(SageObject): non-deterministic). If the transition does not exist, the function should raise a - ``LookupError`` or return an empty list. + :class:`LookupError` or return an empty list. When constructing a finite state machine in this way, some initial states and an input alphabet have to be specified. @@ -5507,7 +5508,7 @@ def state(self, state): The state of the finite state machine corresponding to ``state``. - If no state is found, then a ``LookupError`` is thrown. + If no state is found, then a :class:`LookupError` is thrown. EXAMPLES:: @@ -5553,7 +5554,7 @@ def transition(self, transition): The transition of the finite state machine corresponding to ``transition``. - If no transition is found, then a ``LookupError`` is thrown. + If no transition is found, then a :class:`LookupError` is thrown. EXAMPLES:: @@ -7622,7 +7623,7 @@ def product_FiniteStateMachine(self, other, function, for `j\in\{1, \ldots, d\}` and returns a pair ``(word_in, word_out)`` which is the label of the transition `A=(A_1, \ldots, A_d)` to `B=(B_1, \ldots, B_d)`. If there is no transition from `A` to `B`, - then ``function`` should raise a ``LookupError``. + then ``function`` should raise a :class:`LookupError`. - ``new_input_alphabet`` (optional) -- the new input alphabet as a list. @@ -7637,7 +7638,7 @@ def product_FiniteStateMachine(self, other, function, the corresponding state in the new finite state machine. By default, the final output is the empty word if both final outputs of the constituent states are empty; otherwise, a - ``ValueError`` is raised. + :class:`ValueError` is raised. - ``new_class`` -- Class of the new finite state machine. By default (``None``), the class of ``self`` is used. @@ -12685,7 +12686,7 @@ def process(self, *args, **kwargs): - ``full_output`` -- (default: ``True``) a boolean. If set, then the full output is given, otherwise only the generated output (the third entry below only). If the input is not - accepted, a ``ValueError`` is raised. + accepted, a :class:`ValueError` is raised. - ``always_include_output`` -- if set (not by default), always include the output. This is inconsequential for a diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 3b8cb966702..cec67892f4c 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -3017,7 +3017,7 @@ def arm_length(self, i, j): OUTPUT: - An integer or a ``ValueError`` + An integer or a :class:`ValueError` EXAMPLES:: @@ -3111,7 +3111,7 @@ def leg_length(self, i, j): OUTPUT: - An integer or a ``ValueError`` + An integer or a :class:`ValueError` EXAMPLES:: @@ -6201,8 +6201,9 @@ def __contains__(self, x): def subset(self, *args, **kwargs): r""" - Return ``self`` if no arguments are given, otherwise raises a - ``ValueError``. + Return ``self`` if no arguments are given. + + Otherwise, it raises a :class:`ValueError`. EXAMPLES:: @@ -9017,7 +9018,7 @@ def number_of_partitions(n, algorithm='default'): sage: len(v) 7 - The input must be a nonnegative integer or a ``ValueError`` is raised. + The input must be a nonnegative integer or a :class:`ValueError` is raised. :: diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index b5caf31d84b..84c77b0670e 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -36,7 +36,7 @@ def _int_or_half_int(k): OUTPUT: - If ``k`` is not in `1/2 \ZZ`, then this raises a ``ValueError``. + If ``k`` is not in `1/2 \ZZ`, then this raises a :class:`ValueError`. Otherwise, we return the pair: - boolean; ``True`` if ``k`` is an integer and ``False`` if a half integer diff --git a/src/sage/combinat/partition_shifting_algebras.py b/src/sage/combinat/partition_shifting_algebras.py index e8f22589eb3..90f1f10d59f 100644 --- a/src/sage/combinat/partition_shifting_algebras.py +++ b/src/sage/combinat/partition_shifting_algebras.py @@ -92,7 +92,7 @@ def check(self, seq): r""" Verify that ``seq`` is a valid shifting sequence. - If it is not, raise a ``ValueError``. + If it is not, raise a :class:`ValueError`. EXAMPLES:: diff --git a/src/sage/combinat/words/word_infinite_datatypes.py b/src/sage/combinat/words/word_infinite_datatypes.py index 9998d3487d5..604acfb3c1b 100644 --- a/src/sage/combinat/words/word_infinite_datatypes.py +++ b/src/sage/combinat/words/word_infinite_datatypes.py @@ -862,7 +862,7 @@ def __getitem__(self, key): step = 1 if key.step is None else int(key.step) # If either key.start or key.stop is negative, # then we need to expand the word. - if start < 0 or (not (stop is None) and stop < 0): + if start < 0 or (stop is not None and stop < 0): data = list(self)[key] length = None # If key.step is negative, then we need to expand a prefix. From 59396b23e46e3147919462bc5d37c89e82a0da1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Feb 2024 12:05:00 +0100 Subject: [PATCH 146/191] minor details in modular --- src/sage/modular/arithgroup/arithgroup_generic.py | 2 +- src/sage/modular/arithgroup/arithgroup_perm.py | 4 ++-- src/sage/modular/arithgroup/congroup_gammaH.py | 4 ++-- src/sage/modular/btquotients/btquotient.py | 2 +- src/sage/modular/hecke/ambient_module.py | 2 +- src/sage/modular/hecke/module.py | 8 ++++---- src/sage/modular/hecke/submodule.py | 4 ++-- src/sage/modular/local_comp/smoothchar.py | 2 +- src/sage/modular/modform/constructor.py | 2 +- src/sage/modular/modform/space.py | 4 ++-- src/sage/modular/modsym/apply.pyx | 2 +- src/sage/modular/modsym/element.py | 2 +- src/sage/modular/modsym/ghlist.py | 2 +- src/sage/modular/modsym/hecke_operator.py | 4 ++-- src/sage/modular/overconvergent/genus0.py | 2 +- src/sage/modular/pollack_stevens/distributions.py | 6 +++--- src/sage/modular/pollack_stevens/fund_domain.py | 6 +++--- src/sage/modular/pollack_stevens/manin_map.py | 6 +++--- src/sage/modular/pollack_stevens/space.py | 10 +++++----- 19 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py index 0547901b1f1..a208688ee62 100644 --- a/src/sage/modular/arithgroup/arithgroup_generic.py +++ b/src/sage/modular/arithgroup/arithgroup_generic.py @@ -571,7 +571,7 @@ def is_subgroup(self, right): w = self.gens() for g in w: - if not (g in right): + if g not in right: return False return True diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index fdb51078898..6ff94e3bcac 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -816,7 +816,7 @@ def relabel(self, inplace=True): """ if hasattr(self,'_canonical_label_group'): if inplace: - if not (self is self._canonical_label_group): + if self is not self._canonical_label_group: self.__dict__ = self._canonical_label_group.__dict__ self._canonical_label_group = self else: @@ -1811,7 +1811,7 @@ def cusp_widths(self,exp=False): c1 = min(L.orbit(inv(c0))) cusps.remove(c1) if exp: - if not len(c) in widths: + if len(c) not in widths: widths[len(c)] = 0 widths[len(c)] += 1 else: diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index 9310ecae804..02794c269ad 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -1045,7 +1045,7 @@ def is_subgroup(self, other): # difficult case t = other._list_of_elements_in_H() for x in self._generators_for_H(): - if not (x in t): + if x not in t: return False return True @@ -1402,7 +1402,7 @@ def _list_subgroup(N, gens): raise ValueError("gen (=%s) is not in (Z/%sZ)^*" % (g, N)) gk = int(g) % N sbgrp = [gk] - while not (gk in H): + while gk not in H: gk = (gk * g) % N sbgrp.append(gk) H = {(x * h) % N for x in sbgrp for h in H} diff --git a/src/sage/modular/btquotients/btquotient.py b/src/sage/modular/btquotients/btquotient.py index 155fb2ebb2e..828a003b0ab 100644 --- a/src/sage/modular/btquotients/btquotient.py +++ b/src/sage/modular/btquotients/btquotient.py @@ -695,7 +695,7 @@ def edge_between_vertices(self, v1, v2, normalized=False): - 2x2 integer matrix, representing the edge from ``v1`` to ``v2``. If ``v1`` and ``v2`` are not at distance `1`, raise - a ``ValueError``. + a :class:`ValueError`. EXAMPLES:: diff --git a/src/sage/modular/hecke/ambient_module.py b/src/sage/modular/hecke/ambient_module.py index 4d0b4c583ea..1ada75090e6 100644 --- a/src/sage/modular/hecke/ambient_module.py +++ b/src/sage/modular/hecke/ambient_module.py @@ -392,7 +392,7 @@ def degeneracy_map(self, codomain, t=1): "level (=%s) and t (=%s) must be a divisor of the quotient") % (self.level(), level, t)) eps = self.character() - if not (eps is None) and level % eps.conductor() != 0: + if eps is not None and level % eps.conductor() != 0: raise ArithmeticError("the conductor of the character of this space " "(=%s) must be divisible by the level (=%s)" % (eps.conductor(), level)) diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index d0d8d5e49d7..b71cec39c22 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -456,7 +456,7 @@ def rank(self): r""" Return the rank of this module over its base ring. - This raises a ``NotImplementedError``, since this is an + This raises a :class:`NotImplementedError`, since this is an abstract base class. EXAMPLES:: @@ -473,7 +473,7 @@ def submodule(self, X): Return the submodule of ``self`` corresponding to ``X``. As this is an abstract base class, this raises a - ``NotImplementedError``. + :class:`NotImplementedError`. EXAMPLES:: @@ -739,7 +739,7 @@ def ambient_hecke_module(self): r""" Return the ambient module associated to this module. - As this is an abstract base class, raise ``NotImplementedError``. + As this is an abstract base class, raise :class:`NotImplementedError`. EXAMPLES:: @@ -1501,7 +1501,7 @@ def is_simple(self): Return ``True`` if this space is simple as a module for the corresponding Hecke algebra. - Raises ``NotImplementedError``, as this is an abstract base + This raises :class:`NotImplementedError`, as this is an abstract base class. EXAMPLES:: diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index 379daa00f7a..64791118387 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -95,7 +95,7 @@ def __init__(self, ambient, submodule, dual_free_module=None, check=True): module.HeckeModule_free_module.__init__(self, ambient.base_ring(), ambient.level(), ambient.weight()) - if not (dual_free_module is None): + if dual_free_module is not None: if not is_FreeModule(dual_free_module): raise TypeError("dual_free_module must be a free module") if dual_free_module.rank() != submodule.rank(): @@ -960,7 +960,7 @@ def submodule_from_nonembedded_module(self, V, Vdual=None, check=True): # so fast, and their are asymptotically fast algorithms. A = M_V * M_E V = A.row_space() - if not (Vdual is None): + if Vdual is not None: E = self.dual_free_module() M_Vdual = Vdual.matrix() M_E = E.matrix() diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index 333b609aef6..cfce7550fda 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -802,7 +802,7 @@ def _test_unitgens(self, **options): g = I.small_residue(g) else: # I is an ideal of ZZ g = g % (I.gen()) - if not (g - 1 in I): + if g - 1 not in I: T.fail("For generator g=%s, g^%s = %s, which is not 1 mod I" % (gens[i], exps[i], g)) I = self.prime() if self.number_field() == QQ else self.ideal(1) T.assertEqual(gens[-1].valuation(I), 1) diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index 2adde8bafc3..180c6bc920d 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -330,7 +330,7 @@ def ModularForms(group=1, if use_cache and key in _cache: M = _cache[key]() - if not (M is None): + if M is not None: M.set_precision(prec) return M diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index fcfb3b770a0..bd01c29d84d 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -365,7 +365,7 @@ def has_character(self): sage: CuspForms(DirichletGroup(11).0,3).has_character() True """ - return not self.character() is None + return self.character() is not None def is_ambient(self): """ @@ -1929,6 +1929,6 @@ def contains_each(V, B): False """ for b in B: - if not (b in V): + if b not in V: return False return True diff --git a/src/sage/modular/modsym/apply.pyx b/src/sage/modular/modsym/apply.pyx index 4e253c2f805..6017a933a5e 100644 --- a/src/sage/modular/modsym/apply.pyx +++ b/src/sage/modular/modsym/apply.pyx @@ -10,7 +10,7 @@ Monomial expansion of `(aX + bY)^i (cX + dY)^{j-i}` # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ########################################################################## diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index 80ccb3c4242..686e64e96d8 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -76,7 +76,7 @@ def set_modsym_print_mode(mode="manin"): sage: set_modsym_print_mode() """ mode = str(mode).lower() - if not (mode in ['manin', 'modular', 'vector']): + if mode not in ['manin', 'modular', 'vector']: raise ValueError("mode must be one of 'manin', 'modular', or 'vector'") global _print_mode _print_mode = mode diff --git a/src/sage/modular/modsym/ghlist.py b/src/sage/modular/modsym/ghlist.py index 2e74217f851..62989e22105 100644 --- a/src/sage/modular/modsym/ghlist.py +++ b/src/sage/modular/modsym/ghlist.py @@ -16,7 +16,7 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ########################################################################### from sage.structure.richcmp import richcmp_method, richcmp from sage.structure.sage_object import SageObject diff --git a/src/sage/modular/modsym/hecke_operator.py b/src/sage/modular/modsym/hecke_operator.py index 43e34b57a49..818de55e97e 100644 --- a/src/sage/modular/modsym/hecke_operator.py +++ b/src/sage/modular/modsym/hecke_operator.py @@ -9,7 +9,7 @@ # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ########################################################################## @@ -23,7 +23,7 @@ def apply_sparse(self, x): """ Return the image of ``x`` under ``self``. - If ``x`` is not in ``self.domain()``, raise a ``TypeError``. + If ``x`` is not in ``self.domain()``, raise a :class:`TypeError`. EXAMPLES:: diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index 6fd1770c8bf..58f31f4af0c 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -261,7 +261,7 @@ def OverconvergentModularForms(prime, weight, radius, base_ring=QQ, prec=20, cha if key in __ocmfdict: w = __ocmfdict[key] M = w() - if not (M is None): + if M is not None: return M M = OverconvergentModularFormsSpace(*key) __ocmfdict[key] = weakref.ref(M) diff --git a/src/sage/modular/pollack_stevens/distributions.py b/src/sage/modular/pollack_stevens/distributions.py index a83121db4fa..528e3d640cf 100644 --- a/src/sage/modular/pollack_stevens/distributions.py +++ b/src/sage/modular/pollack_stevens/distributions.py @@ -31,14 +31,14 @@ (1 + O(11^5), 2 + O(11^4), 3 + O(11^3), 4 + O(11^2), 5 + O(11)) """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # # Distributed under the terms of the GNU General Public License (GPL) # 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 sage.misc.lazy_import import lazy_import from sage.modules.module import Module diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index ef067853f4a..cab712a95bf 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -12,15 +12,15 @@ - Robert Pollack, Jonathan Hanke (2012): initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # Jonathan Hanke # # Distributed under the terms of the GNU General Public License (GPL) # 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 sage.matrix.matrix_space import MatrixSpace from sage.modular.modsym.all import P1List diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index 06aa3496e10..0af3ed946d0 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -34,14 +34,14 @@ 1 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # # Distributed under the terms of the GNU General Public License (GPL) # 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 sage.rings.continued_fraction import convergents from .sigma0 import Sigma0 diff --git a/src/sage/modular/pollack_stevens/space.py b/src/sage/modular/pollack_stevens/space.py index 6fe7c12b95b..c0464dd61d8 100644 --- a/src/sage/modular/pollack_stevens/space.py +++ b/src/sage/modular/pollack_stevens/space.py @@ -56,14 +56,14 @@ sage: phi.parent() Space of modular symbols for Congruence Subgroup Gamma0(37) with sign 0 and values in Sym^0 Q^2 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Robert Pollack # # Distributed under the terms of the GNU General Public License (GPL) # 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 sage.modules.module import Module from sage.modular.dirichlet import DirichletCharacter from sage.modular.arithgroup.all import Gamma0 @@ -76,7 +76,7 @@ from .distributions import OverconvergentDistributions, Symk from .modsym import (PSModularSymbolElement, PSModularSymbolElement_symk, - PSModularSymbolElement_dist, PSModSymAction) + PSModularSymbolElement_dist, PSModSymAction) from .manin_map import ManinMap from .sigma0 import Sigma0, Sigma0Element @@ -887,7 +887,7 @@ def ps_modsym_from_elliptic_curve(E, sign=0, implementation='eclib'): sage: symb.values() [-1/6, 1/3, 1/2, 1/6, -1/6, 1/3, -1/3, -1/2, -1/6, 1/6, 0, -1/6, -1/6] """ - if not (E.base_ring() is QQ): + if E.base_ring() is not QQ: raise ValueError("The elliptic curve must be defined over the " "rationals.") sign = Integer(sign) From 7b3bb5b23a22f54ac5b62e35ec8be1925623d9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Feb 2024 12:21:46 +0100 Subject: [PATCH 147/191] fix one doctest --- src/sage/modules/free_module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 7c880f87b29..10343f41b95 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -187,6 +187,7 @@ import sage.rings.integer_ring import sage.rings.rational_field from sage.rings.ring import IntegralDomain, is_Ring +from sage.categories.fields import Fields from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.integral_domains import IntegralDomains from sage.categories.principal_ideal_domains import PrincipalIdealDomains @@ -4347,7 +4348,7 @@ def __init__(self, base_field, dimension, degree, sparse=False, category=None): sage: FreeModule_generic_field(QQ, 5, 5) ) failed: NotImplementedError> """ - if not isinstance(base_field, ring.Field): + if base_field not in Fields(): raise TypeError("The base_field (=%s) must be a field" % base_field) super().__init__(base_field, dimension, degree, sparse=sparse, category=category) From 319cc307bc344a367cf659f4fb40547ccecf601a Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Tue, 6 Feb 2024 16:07:11 +0200 Subject: [PATCH 148/191] Impovements suggested by tscrim --- src/sage/matroids/circuits_matroid.pyx | 88 ++++++++++++++++++-------- src/sage/matroids/matroid.pyx | 5 +- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index 4eedbfcf8b8..d457065d236 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -17,12 +17,6 @@ access to the ``CircuitsMatroid`` constructor, run:: AUTHORS: - Giorgos Mousa (2023-12-23): initial version - -TESTS:: - - sage: from sage.matroids.circuits_matroid import CircuitsMatroid - sage: M = CircuitsMatroid(matroids.catalog.Fano()) - sage: TestSuite(M).run() """ # **************************************************************************** @@ -43,19 +37,16 @@ from cpython.object cimport Py_EQ, Py_NE cdef class CircuitsMatroid(Matroid): r""" + A matroid defined by its circuits. + INPUT: - ``M`` -- a matroid (default: ``None``) - ``groundset`` -- a list (default: ``None``); the groundset of the matroid - ``circuits`` -- a list (default: ``None``); the collection of circuits of the matroid - - OUTPUT: - - - If the input is a matroid ``M``, return a ``CircuitsMatroid`` instance - representing ``M``. - - Otherwise, return a ``CircuitsMatroid`` instance based on the - ``groundset`` and ``circuits``. + - ``nsc_defined`` -- boolean (default: ``False``); whether the matroid was + defined by its nonspanning circuits .. NOTE:: @@ -68,6 +59,12 @@ cdef class CircuitsMatroid(Matroid): """ Initialization of the matroid. See class docstring for full documentation. + + TESTS:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.catalog.Fano()) + sage: TestSuite(M).run() """ if M is not None: self._groundset = frozenset(M.groundset()) @@ -153,8 +150,8 @@ cdef class CircuitsMatroid(Matroid): OUTPUT: boolean """ - I = set(F) - s = len(F) + cdef set I = set(F) + cdef int s = len(F) for i in self._k_C: if i <= s: for C in self._k_C[i]: @@ -173,7 +170,7 @@ cdef class CircuitsMatroid(Matroid): OUTPUT: a frozenset; a maximal independent subset of ``X`` """ - I = set(F) + cdef set I = set(F) for i in self._k_C: for C in self._k_C[i]: if i <= len(I) and i > 0: @@ -196,8 +193,8 @@ cdef class CircuitsMatroid(Matroid): OUTPUT: a frozenset; a circuit contained in ``X``, if it exists. Otherwise an error is raised. """ - I = set(F) - for C in self.circuits(): + cdef set I = set(F) + for C in self._C: if C <= I: return C raise ValueError("no circuit in independent set") @@ -386,6 +383,8 @@ cdef class CircuitsMatroid(Matroid): sage: from sage.matroids.circuits_matroid import CircuitsMatroid sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: len(M.bases()) + 6 """ cdef SetSystem B B = SetSystem(list(self.groundset())) @@ -408,6 +407,9 @@ cdef class CircuitsMatroid(Matroid): sage: from sage.matroids.circuits_matroid import CircuitsMatroid sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: it = M.bases_iterator() + sage: it.__next__() + frozenset({0, 1}) """ from itertools import combinations for B in combinations(self._groundset, self._matroid_rank): @@ -424,6 +426,13 @@ cdef class CircuitsMatroid(Matroid): Return the circuits of the matroid. OUTPUT: a SetSystem + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: M.circuits() + Iterator over a system of subsets """ cdef SetSystem C C = SetSystem(list(self.groundset())) @@ -439,6 +448,13 @@ cdef class CircuitsMatroid(Matroid): def circuits_iterator(self, k=None): """ Return an iterator over the circuits of the matroid. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: sum(1 for C in M.circuits_iterator()) + 4 """ if k: for C in self._k_C[k]: @@ -453,6 +469,13 @@ cdef class CircuitsMatroid(Matroid): Return the list of nonspanning circuits of the matroid. OUTPUT: a SetSystem + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: M.nonspanning_circuits() + Iterator over a system of subsets """ cdef SetSystem NSC NSC = SetSystem(list(self.groundset())) @@ -462,7 +485,7 @@ cdef class CircuitsMatroid(Matroid): NSC.append(C) return NSC - def no_broken_circuits_sets(self, order=None): + cpdef no_broken_circuits_sets(self, order=None) noexcept: r""" Return the no broken circuits (NBC) sets of ``self``. @@ -473,6 +496,8 @@ cdef class CircuitsMatroid(Matroid): - ``order`` -- a total ordering of the groundset given as a list + OUTPUT: a list of frozensets + EXAMPLES:: sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) @@ -489,6 +514,16 @@ cdef class CircuitsMatroid(Matroid): sage: SimplicialComplex(M.no_broken_circuits_sets([5,4,3,2,1])) Simplicial complex with vertex set (1, 2, 3, 4, 5) and facets {(1, 3, 5), (2, 3, 5), (2, 4, 5), (3, 4, 5)} + + TESTS:: + + sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) + sage: C1 = SimplicialComplex(M.no_broken_circuits_sets()) + sage: from sage.matroids.basis_matroid import BasisMatroid + sage: M = BasisMatroid(Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]])) + sage: C2 = SimplicialComplex(M.no_broken_circuits_sets()) + sage: C1 == C2 + True """ if order is None: order = sorted(self.groundset(), key=str) @@ -497,14 +532,14 @@ cdef class CircuitsMatroid(Matroid): raise ValueError("not an ordering of the groundset") # compute broken circuits - BC = [] - for C in self.circuits(): + cdef list BC = [] + for C in self._C: for e in order: if e in C: BC.append(C - set([e])) break - # the facets suffice + cdef list F = [] # broken circuit complex facets for B in self.bases(): flag = True for bc in BC: @@ -512,7 +547,10 @@ cdef class CircuitsMatroid(Matroid): flag = False break if flag: - yield B + F.append(B) + + from sage.topology.simplicial_complex import SimplicialComplex + return [frozenset(f) for f in SimplicialComplex(F).face_iterator()] # properties @@ -532,7 +570,7 @@ cdef class CircuitsMatroid(Matroid): [Oxl2011]_, p. 327. """ - return min([i for i in self._k_C], default=float('inf')) + return min(self._k_C, default=float('inf')) cpdef is_paving(self) noexcept: """ @@ -555,7 +593,7 @@ cdef class CircuitsMatroid(Matroid): cpdef is_valid(self) noexcept: r""" - Test if self obeys the matroid axioms. + Test if ``self`` obeys the matroid axioms. For a matroid defined by its circuits, we check the circuit axioms. diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index b2d86f1e64a..9bd50d2053b 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -2956,9 +2956,12 @@ cdef class Matroid(SageObject): - ``ordering`` -- a total ordering of the groundset given as a list + OUTPUT: a list of frozensets + EXAMPLES:: - sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]]) + sage: from sage.matroids.basis_matroid import BasisMatroid + sage: M = BasisMatroid(Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]])) sage: SimplicialComplex(M.no_broken_circuits_sets()) # needs sage.graphs Simplicial complex with vertex set (1, 2, 3, 4, 5) and facets {(1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5)} From 29a08652a9a185164013fdc49ef4d9462125a0c2 Mon Sep 17 00:00:00 2001 From: gmou3 <32706872+gmou3@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:09:41 +0200 Subject: [PATCH 149/191] Edit amount of empty lines Co-authored-by: Travis Scrimshaw --- src/sage/matroids/constructor.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index f404ca57926..137f4c28230 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -313,8 +313,6 @@ def Matroid(groundset=None, data=None, **kwds): sage: M1 == M2 True - - sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd', ....: 'bcd']) sage: type(M) From 5b795606e48fbe9b6ce4ea4de5790d688ed3d84a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 6 Feb 2024 18:20:44 +0100 Subject: [PATCH 150/191] 37014: fixes according to review --- src/sage/knots/knotinfo.py | 27 +++++++++++++++++++++++---- src/sage/knots/link.py | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index 5398cd71062..822d78c9bd7 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -101,7 +101,7 @@ sage: # optional - snappy sage: L6 = KnotInfo.L6a1_0 sage: l6s = L6.link(snappy=True); l6s - Plink failed to import tkinter. + ... sage: type(l6s) @@ -264,8 +264,14 @@ def eval_knotinfo(string, locals={}, to_tuple=True): '{3, {-2, -2, -1, 2, -1}}' sage: eval_knotinfo(_) (3, (-2, -2, -1, 2, -1)) + sage: KnotInfo.K13a_1.kauffman_polynomial() # optional - database_knotinfo # indirect doctest + Traceback (most recent call last): + ... + NotImplementedError: This value is not provided by the database """ if not string: + # An empty string in the database Excel spreadsheet indicates that + # the property is not provided for that particular knot or link. raise NotImplementedError('This value is not provided by the database') if to_tuple: new_string = string.replace('{', '(') @@ -289,12 +295,14 @@ def knotinfo_int(string): sage: from sage.knots.knotinfo import knotinfo_int sage: knotinfo_int('7') 7 - sage: knotinfo_int('') + sage: KnotInfo.K13a_1.braid_index() # optional - database_knotinfo # indirect doctest Traceback (most recent call last): ... NotImplementedError: This integer is not provided by the database """ if not string: + # an empty string in the Excel sheet of the database indicates that + # the property is not provided for this special knot or link. raise NotImplementedError('This integer is not provided by the database') else: return int(string) @@ -312,8 +320,14 @@ def knotinfo_bool(string): sage: from sage.knots.knotinfo import knotinfo_bool sage: knotinfo_bool('Y') True + sage: KnotInfo.K13a_1.is_almost_alternating() # optional - database_knotinfo # indirect doctest + Traceback (most recent call last): + ... + NotImplementedError: This boolean is not provided by the database """ if not string: + # an empty string in the Excel sheet of the database indicates that + # the property is not provided for this special knot or link. raise NotImplementedError('This boolean is not provided by the database') if string == 'Y': return True @@ -1684,7 +1698,7 @@ def conway_polynomial(self, var='t', original=False): return R(eval_knotinfo(conway_polynomial, locals=lc)) @cached_method - def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, reduced=False, odd=False): + def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, reduced=False, odd=False, KhoHo=False): r""" Return the Khovanov polynomial according to the value of column ``khovanov_polynomial`` for this knot or link as an instance of @@ -1702,6 +1716,8 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, the reduced version of the homology is used - ``odd`` -- boolean (default: ``False``); if set to ``True`` the odd version of the homology is used + - ``KhoHo`` -- boolean (deprecated). The corresponding values have + disappeared from the database since January 2024 OUTPUT: @@ -1800,12 +1816,15 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, """ ch = base_ring.characteristic() integral = ch == 0 and base_ring.is_field() - KhoHo = False if not self.is_knot(): # KnotJob calculated results only available for knots khovanov_polynomial = self[self.items.khovanov_polynomial] KhoHo = True else: + if KhoHo: + KhoHo = False + from sage.misc.superseded import deprecation + deprecation(37014, "The KhoHo option is deprecated and ignored.") if reduced: if integral: khovanov_polynomial = self[self.items.khovanov_reduced_integral_polynomial] diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 3f181184152..36f6e22c6c8 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -4185,7 +4185,7 @@ def get_knotinfo(self, mirror_version=True, unique=True): `__:: sage: import snappy # optional - snappy - Plink failed to import tkinter. + ... sage: # optional - database_knotinfo snappy sage: from sage.knots.knotinfo import KnotInfoSeries From 3767c65bb022ed3a0c18efd60cd9f7fcf7a191ec Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Tue, 6 Feb 2024 21:14:59 +0200 Subject: [PATCH 151/191] Update circuits_matroid.p* Change 'order' to 'ordering'; use 'ordering' for consistency with matroid.p*. Add nonspanning_circuits_iterator() and improve docstrings. --- src/sage/matroids/circuits_matroid.pxd | 1 + src/sage/matroids/circuits_matroid.pyx | 45 ++++++++++++++++++++------ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pxd b/src/sage/matroids/circuits_matroid.pxd index 2452e639726..2de06bd16ab 100644 --- a/src/sage/matroids/circuits_matroid.pxd +++ b/src/sage/matroids/circuits_matroid.pxd @@ -18,6 +18,7 @@ cdef class CircuitsMatroid(Matroid): cpdef bases(self) noexcept cpdef circuits(self, k=*) noexcept cpdef nonspanning_circuits(self) noexcept + cpdef no_broken_circuits_sets(self, ordering=*) noexcept # properties cpdef girth(self) noexcept diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index d457065d236..46e0b989d95 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -2,15 +2,16 @@ r""" Circuits matroids Matroids are characterized by a list of circuits, which are minimal dependent -sets. The CircuitsMatroid class implements matroids using this information as -data. +sets. The ``CircuitsMatroid`` class implements matroids using this information +as data. A ``CircuitsMatroid`` can be created from another matroid or from a list of circuits. For a full description of allowed inputs, see :class:`below `. It is recommended to use the :func:`Matroid() ` -function for a more flexible construction of a ``CircuitsMatroid``. For direct -access to the ``CircuitsMatroid`` constructor, run:: +function for a more flexible way of constructing a ``CircuitsMatroid`` and +other classes of matroids. For direct access to the ``CircuitsMatroid`` +constructor, run:: sage: from sage.matroids.circuits_matroid import CircuitsMatroid @@ -425,6 +426,10 @@ cdef class CircuitsMatroid(Matroid): """ Return the circuits of the matroid. + INPUT: + + - ``k`` -- an integer (optional); the length of the circuits + OUTPUT: a SetSystem EXAMPLES:: @@ -449,6 +454,10 @@ cdef class CircuitsMatroid(Matroid): """ Return an iterator over the circuits of the matroid. + INPUT: + + - ``k`` -- an integer (optional); the length of the circuits + EXAMPLES:: sage: from sage.matroids.circuits_matroid import CircuitsMatroid @@ -485,7 +494,23 @@ cdef class CircuitsMatroid(Matroid): NSC.append(C) return NSC - cpdef no_broken_circuits_sets(self, order=None) noexcept: + def nonspanning_circuits_iterator(self): + """ + Return an iterator over the nonspanning circuits of the matroid. + + EXAMPLES:: + + sage: from sage.matroids.circuits_matroid import CircuitsMatroid + sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) + sage: list(M.nonspanning_circuits_iterator()) + [] + """ + for i in self._k_C: + if i <= self.rank(): + for C in self._k_C[i]: + yield C + + cpdef no_broken_circuits_sets(self, ordering=None) noexcept: r""" Return the no broken circuits (NBC) sets of ``self``. @@ -494,7 +519,7 @@ cdef class CircuitsMatroid(Matroid): INPUT: - - ``order`` -- a total ordering of the groundset given as a list + - ``ordering`` -- a total ordering of the groundset given as a list OUTPUT: a list of frozensets @@ -525,16 +550,16 @@ cdef class CircuitsMatroid(Matroid): sage: C1 == C2 True """ - if order is None: - order = sorted(self.groundset(), key=str) + if ordering is None: + ordering = sorted(self.groundset(), key=str) else: - if frozenset(order) != self.groundset(): + if frozenset(ordering) != self.groundset(): raise ValueError("not an ordering of the groundset") # compute broken circuits cdef list BC = [] for C in self._C: - for e in order: + for e in ordering: if e in C: BC.append(C - set([e])) break From ef0dbbeb590f1f3243c057b5ecd365c544635e87 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Feb 2024 22:36:16 -0800 Subject: [PATCH 152/191] .github/workflows: Upgrade easimon/maximize-build-space to v10 --- .github/workflows/docker.yml | 2 +- .github/workflows/docker_hub.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9c9c033441f..8dd83a8a302 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -152,7 +152,7 @@ jobs: EXTRA_SAGE_PACKAGES: ${{ inputs.extra_sage_packages }} steps: - name: Maximize build disk space - uses: easimon/maximize-build-space@v8 + uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images root-reserve-mb: 40000 diff --git a/.github/workflows/docker_hub.yml b/.github/workflows/docker_hub.yml index 694cd51762f..8c3d2fff12b 100644 --- a/.github/workflows/docker_hub.yml +++ b/.github/workflows/docker_hub.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Maximize build disk space - uses: easimon/maximize-build-space@v8 + uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images root-reserve-mb: 40000 From 78ed70efc7d36420744971afda72ea708be88410 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Feb 2024 22:40:33 -0800 Subject: [PATCH 153/191] .github/workflows: Ignore failures in easimon/maximize-build-space --- .github/workflows/docker.yml | 1 + .github/workflows/docker_hub.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8dd83a8a302..55b76a99523 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,6 +161,7 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + continue-on-error: true if: inputs.free_disk_space - name: Check out SageMath uses: actions/checkout@v4 diff --git a/.github/workflows/docker_hub.yml b/.github/workflows/docker_hub.yml index 8c3d2fff12b..b71eec8178d 100644 --- a/.github/workflows/docker_hub.yml +++ b/.github/workflows/docker_hub.yml @@ -25,6 +25,7 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true + continue-on-error: true - name: Checkout uses: actions/checkout@v4 From a36841b1c46cb32bac46c55e370b74c9b2bbaba3 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 7 Feb 2024 08:05:05 +0100 Subject: [PATCH 154/191] 37014: fix upper cases in error messages --- src/sage/knots/knotinfo.py | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index 822d78c9bd7..2cc94c795fb 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -267,12 +267,12 @@ def eval_knotinfo(string, locals={}, to_tuple=True): sage: KnotInfo.K13a_1.kauffman_polynomial() # optional - database_knotinfo # indirect doctest Traceback (most recent call last): ... - NotImplementedError: This value is not provided by the database + NotImplementedError: this value is not provided by the database """ if not string: # An empty string in the database Excel spreadsheet indicates that # the property is not provided for that particular knot or link. - raise NotImplementedError('This value is not provided by the database') + raise NotImplementedError('this value is not provided by the database') if to_tuple: new_string = string.replace('{', '(') new_string = new_string.replace('}', ')') @@ -298,12 +298,12 @@ def knotinfo_int(string): sage: KnotInfo.K13a_1.braid_index() # optional - database_knotinfo # indirect doctest Traceback (most recent call last): ... - NotImplementedError: This integer is not provided by the database + NotImplementedError: this integer is not provided by the database """ if not string: # an empty string in the Excel sheet of the database indicates that # the property is not provided for this special knot or link. - raise NotImplementedError('This integer is not provided by the database') + raise NotImplementedError('this integer is not provided by the database') else: return int(string) @@ -323,12 +323,12 @@ def knotinfo_bool(string): sage: KnotInfo.K13a_1.is_almost_alternating() # optional - database_knotinfo # indirect doctest Traceback (most recent call last): ... - NotImplementedError: This boolean is not provided by the database + NotImplementedError: this boolean is not provided by the database """ if not string: # an empty string in the Excel sheet of the database indicates that # the property is not provided for this special knot or link. - raise NotImplementedError('This boolean is not provided by the database') + raise NotImplementedError('this boolean is not provided by the database') if string == 'Y': return True elif string == 'N': @@ -423,14 +423,14 @@ def __getitem__(self, item): sage: L[0] Traceback (most recent call last): ... - KeyError: "Item must be an instance of " + KeyError: "item must be an instance of " """ if not isinstance(item, KnotInfoColumns): - raise KeyError('Item must be an instance of %s' % (KnotInfoColumns)) + raise KeyError('item must be an instance of %s' % (KnotInfoColumns)) if item.column_type() == item.types.OnlyLinks and self.is_knot(): - raise KeyError('Item not available for knots' % (KnotInfoColumns)) + raise KeyError('item not available for knots' % (KnotInfoColumns)) if item.column_type() == item.types.OnlyKnots and not self.is_knot(): - raise KeyError('Item not available for links' % (KnotInfoColumns)) + raise KeyError('item not available for links' % (KnotInfoColumns)) l = db.read(item) ind = db.read_row_dict()[self.name][0] @@ -701,7 +701,7 @@ def braid_index(self): sage: K13a_1.braid_index() # optional - database_knotinfo Traceback (most recent call last): ... - NotImplementedError: This integer is not provided by the database + NotImplementedError: this integer is not provided by the database """ if self.is_knot(): @@ -929,7 +929,7 @@ class of an oriented pair, `K = (S_3, S_1)`, with `S_i` ('K6_3', 'fully amphicheiral')] """ if not self.is_knot(): - raise NotImplementedError('This is only available for knots') + raise NotImplementedError('this is only available for knots') symmetry_type = self[self.items.symmetry_type].strip() # for example K10_88 is a case with trailing whitespaces if not symmetry_type and self.crossing_number() == 0: @@ -1798,11 +1798,11 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, sage: K0_1.khovanov_polynomial(base_ring=GF(3), reduced=True) Traceback (most recent call last): ... - ValueError: Characteristic 3 of base ring is not valid + ValueError: characteristic 3 of base ring is not valid sage: K0_1.khovanov_polynomial(base_ring=GF(3), odd=True) Traceback (most recent call last): ... - ValueError: Characteristic 3 of base ring is not valid + ValueError: characteristic 3 of base ring is not valid sage: L.khovanov_polynomial(base_ring=GF(2)) Traceback (most recent call last): ... @@ -1824,7 +1824,7 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, if KhoHo: KhoHo = False from sage.misc.superseded import deprecation - deprecation(37014, "The KhoHo option is deprecated and ignored.") + deprecation(37014, "the KhoHo option is deprecated and ignored.") if reduced: if integral: khovanov_polynomial = self[self.items.khovanov_reduced_integral_polynomial] @@ -1833,7 +1833,7 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, elif ch == 2: khovanov_polynomial = self[self.items.khovanov_reduced_mod2_polynomial] else: - raise ValueError('Characteristic %s of base ring is not valid' % ch) + raise ValueError('characteristic %s of base ring is not valid' % ch) elif odd: if integral: khovanov_polynomial = self[self.items.khovanov_odd_integral_polynomial] @@ -1842,7 +1842,7 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ, original=False, elif ch == 2: khovanov_polynomial = self[self.items.khovanov_odd_mod2_polynomial] else: - raise ValueError('Characteristic %s of base ring is not valid' % ch) + raise ValueError('characteristic %s of base ring is not valid' % ch) else: khovanov_polynomial = self[self.items.khovanov_unreduced_integral_polynomial] @@ -1952,7 +1952,7 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): sage: L.link(use_item=L.items.dt_notation) Traceback (most recent call last): ... - ValueError: Link construction using Columns.dt_notation not possible + ValueError: link construction using Columns.dt_notation not possible using ``snappy``:: @@ -2012,7 +2012,7 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): try: from snappy import Link except ImportError: - raise ImportError('This option demands snappy to be installed') + raise ImportError('this option demands snappy to be installed') elif self.is_knot(): from sage.knots.knot import Knot as Link else: @@ -2034,7 +2034,7 @@ def link(self, use_item=db.columns().pd_notation, snappy=False): elif use_item == self.items.gauss_notation: return Knots().from_gauss_code(self.gauss_notation()) - raise ValueError('Link construction using %s not possible' % use_item) + raise ValueError('link construction using %s not possible' % use_item) @cached_method def is_unique(self): @@ -2506,11 +2506,11 @@ def __getitem__(self, item): """ from sage.rings.integer import Integer if not type(item) in (int, Integer): - raise ValueError('Item must be an integer') + raise ValueError('item must be an integer') l = self.list() max_item = len(l) if item < 0 or item > max_item: - raise ValueError('Item must be non negative and smaller than %s' % (max_item)) + raise ValueError('item must be non negative and smaller than %s' % (max_item)) return l[item] @@ -2549,11 +2549,11 @@ def __call__(self, item): from sage.rings.integer import Integer if not type(item) in (int, Integer): - raise ValueError('Item must be an integer') + raise ValueError('item must be an integer') l = self.list() max_item = len(l) + 1 if item < 1 or item > max_item: - raise ValueError('Item must be positive and smaller than %s' % (max_item)) + raise ValueError('item must be positive and smaller than %s' % (max_item)) return l[item-1] From e387d2ef9ae693603da8fae024248ba33ae968c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Feb 2024 11:12:09 +0100 Subject: [PATCH 155/191] fix suggested details and a few more --- src/sage/coding/cyclic_code.py | 7 ++++--- src/sage/coding/grs_code.py | 21 +++++++++++---------- src/sage/coding/linear_code.py | 8 ++++---- src/sage/coding/linear_code_no_metric.py | 7 ++----- src/sage/coding/punctured_code.py | 2 +- src/sage/coding/reed_muller_code.py | 8 ++++---- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/sage/coding/cyclic_code.py b/src/sage/coding/cyclic_code.py index 920ce21d3d6..8cab59c2504 100644 --- a/src/sage/coding/cyclic_code.py +++ b/src/sage/coding/cyclic_code.py @@ -577,16 +577,17 @@ def field_embedding(self): To: Finite Field in z3 of size 2^3 Defn: 1 |--> 1 """ - if not (hasattr(self, "_field_embedding")): + if not hasattr(self, "_field_embedding"): self.defining_set() return self._field_embedding def defining_set(self, primitive_root=None): r""" Return the set of exponents of the roots of ``self``'s generator - polynomial over the extension field. Of course, it depends on the - choice of the primitive root of the splitting field. + polynomial over the extension field. + Of course, it depends on the choice of the primitive root of + the splitting field. INPUT: diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 0b077c8fbee..56487aa6da1 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -1229,14 +1229,15 @@ def _decode_to_code_and_message(self, r): col_mults = C.column_multipliers() r_list = copy(r) - r_list = [r[i]/col_mults[i] for i in range(0, C.length())] - - t = (C.minimum_distance()-1) // 2 - l0 = n-1-t - l1 = n-1-t-(k-1) - S = matrix(C.base_field(), n, l0+l1+2, - lambda i, j: (C.evaluation_points()[i])**j if j < (l0+1) - else r_list[i]*(C.evaluation_points()[i])**(j-(l0+1))) + r_list = [r[i] / col_mults[i] for i in range(C.length())] + + t = (C.minimum_distance() - 1) // 2 + l0 = n - 1 - t + l1 = n - t - k + pts = C.evaluation_points() + S = matrix(C.base_field(), n, l0 + l1 + 2, + lambda i, j: (pts[i]**j if j < (l0 + 1) + else r_list[i] * pts[i]**(j - (l0 + 1)))) S = S.right_kernel() S = S.basis_matrix().row(0) R = C.base_field()['x'] @@ -1574,7 +1575,7 @@ def _partial_xgcd(self, a, b, PolRing): r = b prev_r = a - while (r.degree() >= stop): + while r.degree() >= stop: q = prev_r.quo_rem(r)[0] (prev_r, r) = (r, prev_r - q * r) (prev_s, s) = (s, prev_s - q * s) @@ -2165,7 +2166,7 @@ def _partial_xgcd(self, a, b, PolRing): prev_r = a r = b - while (r.degree() >= t.degree()): + while r.degree() >= t.degree(): q = prev_r.quo_rem(r)[0] prev_r, r = r, prev_r - q * r prev_t, t = t, prev_t - q * t diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 9d245e7d1b5..885fd02fa95 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -877,7 +877,7 @@ def chinen_polynomial(self): P = RT(C.zeta_polynomial())*T**(d - dperp) if is_even(n): Pd = q**(k-n/2)*RT(Cd.zeta_polynomial()) - if not (is_even(n)): + if not is_even(n): Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial()) CP = P+Pd f = CP/CP(1,s) @@ -886,7 +886,7 @@ def chinen_polynomial(self): P = RT(C.zeta_polynomial()) if is_even(n): Pd = q**(k-n/2)*RT(Cd.zeta_polynomial()) - if not (is_even(n)): + if not is_even(n): Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial()) CP = P+Pd f = CP/CP(1,s) @@ -1666,7 +1666,7 @@ def permutation_automorphism_group(self, algorithm="partition"): gens = Gp.GeneratorsOfGroup() stop = 1 # get ready to stop for x in gens: # if one of these gens is not an auto then don't stop - if not (self.is_permutation_automorphism(Sn_sage(x))): + if not self.is_permutation_automorphism(Sn_sage(x)): stop = 0 break G = PermutationGroup(list(map(Sn_sage, gens))) @@ -1877,7 +1877,7 @@ def weight_distribution(self, algorithm=None): from sage.coding.binary_code import weight_dist return weight_dist(self.generator_matrix()) elif algorithm == "leon": - if not (F.order() in [2,3,5,7]): + if F.order() not in [2, 3, 5, 7]: raise NotImplementedError("The algorithm 'leon' is only implemented for q = 2,3,5,7.") # The GAP command DirectoriesPackageLibrary tells the location of the latest # version of the Guava libraries, so gives us the location of the Guava binaries too. diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py index 939e88f105c..340887789fb 100644 --- a/src/sage/coding/linear_code_no_metric.py +++ b/src/sage/coding/linear_code_no_metric.py @@ -891,12 +891,9 @@ def is_subcode(self, other): True """ G = self.generator_matrix() - for r in G.rows(): - if not (r in other): - return False - return True + return all(r in other for r in G.rows()) - def is_permutation_automorphism(self,g): + def is_permutation_automorphism(self, g): r""" Return `1` if `g` is an element of `S_n` (`n` = length of ``self``) and if `g` is an automorphism of ``self``. diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index 5272ca6c8d9..3caab922b1b 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -328,7 +328,7 @@ def structured_representation(self): C = self.original_code() pts = copy(self.punctured_positions()) list_pts = list(pts) - while (isinstance(C, PuncturedCode)): + while isinstance(C, PuncturedCode): cur_pts = list(C.punctured_positions()) list_len = len(list_pts) for p in cur_pts: diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 44c64ab0d94..1d55ba6fa38 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -258,9 +258,9 @@ def __init__(self, base_field, order, num_of_var): # input sanitization if base_field not in FiniteFields(): raise ValueError("the input `base_field` must be a FiniteField") - if not (isinstance(order, (Integer, int))): + if not isinstance(order, (Integer, int)): raise ValueError("The order of the code must be an integer") - if not (isinstance(num_of_var, (Integer, int))): + if not isinstance(num_of_var, (Integer, int)): raise ValueError("The number of variables must be an integer") q = base_field.cardinality() if order >= q: @@ -425,9 +425,9 @@ def __init__(self, order, num_of_var): ValueError: The order of the code must be an integer """ # input sanitization - if not (isinstance(order, (Integer, int))): + if not isinstance(order, (Integer, int)): raise ValueError("The order of the code must be an integer") - if not (isinstance(num_of_var, (Integer, int))): + if not isinstance(num_of_var, (Integer, int)): raise ValueError("The number of variables must be an integer") if (num_of_var < order): raise ValueError( From d5646693218d2110c65583be1deef55f7f8224ba Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 7 Feb 2024 15:17:58 +0200 Subject: [PATCH 156/191] More suggestions by tscrim and docstring improvements More examples and other mostly minor improvements Docstring improvements: 1-line references and outputs, 95 column limit in examples --- src/sage/matroids/circuits_matroid.pyx | 123 ++++++-- src/sage/matroids/database_collections.py | 12 +- src/sage/matroids/database_matroids.py | 368 +++++++--------------- src/sage/matroids/matroid.pyx | 16 +- 4 files changed, 207 insertions(+), 312 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index 46e0b989d95..5ab01ab74b3 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -150,6 +150,14 @@ cdef class CircuitsMatroid(Matroid): a subset of ``self.groundset()`` OUTPUT: boolean + + EXAMPLES:: + + sage: M = matroids.Theta(4) + sage: M._is_independent(['y0', 'y1', 'y3', 'x2']) + False + sage: M._is_independent(['y0', 'y2', 'y3', 'x2']) + True """ cdef set I = set(F) cdef int s = len(F) @@ -170,14 +178,19 @@ cdef class CircuitsMatroid(Matroid): a subset of ``self.groundset()`` OUTPUT: a frozenset; a maximal independent subset of ``X`` + + EXAMPLES:: + + sage: M = matroids.Theta(6) + sage: len(M._max_independent(M.groundset())) + 6 """ cdef set I = set(F) for i in self._k_C: for C in self._k_C[i]: if i <= len(I) and i > 0: if C <= I: - for e in C: - break + e = next(iter(C)) I.remove(e) return frozenset(I) @@ -193,6 +206,16 @@ cdef class CircuitsMatroid(Matroid): OUTPUT: a frozenset; a circuit contained in ``X``, if it exists. Otherwise an error is raised. + + EXAMPLES:: + + sage: M = matroids.Theta(4) + sage: sorted(M._circuit(['y0', 'y1', 'y3', 'x2'])) + ['x2', 'y0', 'y1', 'y3'] + sage: M._circuit(['y0', 'y2', 'y3', 'x2']) + Traceback (most recent call last): + ... + ValueError: no circuit in independent set """ cdef set I = set(F) for C in self._C: @@ -212,6 +235,17 @@ cdef class CircuitsMatroid(Matroid): OUTPUT: boolean, and, if certificate = True, a dictionary giving the isomorphism or None + EXAMPLES:: + + sage: M = matroids.Spike(3) + sage: from sage.matroids.basis_matroid import BasisMatroid + sage: N = BasisMatroid(M) + sage: M.is_isomorphic(N) + True + sage: N = matroids.catalog.Vamos() + sage: M.is_isomorphic(N) + False + .. NOTE:: Internal version that does no input checking. @@ -226,9 +260,16 @@ cdef class CircuitsMatroid(Matroid): def _repr_(self): """ Return a string representation of the matroid. + + EXAMPLES:: + + sage: matroids.Theta(10) + Theta_10: Matroid of rank 10 on 20 elements with 490 circuits + sage: matroids.catalog.NonDesargues() + NonDesargues: Matroid of rank 3 on 10 elements with 9 nonspanning circuits """ if self._nsc_defined: - return Matroid._repr_(self) + " with " + str(len(self.nonspanning_circuits())) + " non-spanning circuits" + return Matroid._repr_(self) + " with " + str(len(self.nonspanning_circuits())) + " nonspanning circuits" else: return Matroid._repr_(self) + " with " + str(len(self._C)) + " circuits" @@ -387,13 +428,16 @@ cdef class CircuitsMatroid(Matroid): sage: len(M.bases()) 6 """ - cdef SetSystem B + cdef SetSystem B, NSC + cdef bint flag B = SetSystem(list(self.groundset())) + NSC = self.nonspanning_circuits() from itertools import combinations for S in combinations(self._groundset, self._matroid_rank): flag = True - for C in self.nonspanning_circuits(): - if C <= set(S): + S = frozenset(S) + for C in NSC: + if C <= S: flag = False break if flag: @@ -411,16 +455,20 @@ cdef class CircuitsMatroid(Matroid): sage: it = M.bases_iterator() sage: it.__next__() frozenset({0, 1}) + sage: sorted(M.bases_iterator(), key=str) + [frozenset({0, 1}), + frozenset({0, 2}), + frozenset({0, 3}), + frozenset({1, 2}), + frozenset({1, 3}), + frozenset({2, 3})] """ from itertools import combinations + cdef SetSystem NSC = self.nonspanning_circuits() for B in combinations(self._groundset, self._matroid_rank): - flag = True - for C in self.nonspanning_circuits(): - if C <= set(B): - flag = False - break - if flag: - yield frozenset(B) + B = frozenset(B) + if not any(C <= B for C in NSC): + yield B cpdef circuits(self, k=None) noexcept: """ @@ -438,12 +486,20 @@ cdef class CircuitsMatroid(Matroid): sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) sage: M.circuits() Iterator over a system of subsets + sage: list(M.circuits(0)) + [] + sage: sorted(M.circuits(3), key=str) + [frozenset({0, 1, 2}), + frozenset({0, 1, 3}), + frozenset({0, 2, 3}), + frozenset({1, 2, 3})] """ cdef SetSystem C C = SetSystem(list(self.groundset())) - if k: - for c in self._k_C[k]: - C.append(c) + if k is not None: + if k in self._k_C: + for c in self._k_C[k]: + C.append(c) else: for i in self._k_C: for c in self._k_C[i]: @@ -464,10 +520,18 @@ cdef class CircuitsMatroid(Matroid): sage: M = CircuitsMatroid(matroids.Uniform(2, 4)) sage: sum(1 for C in M.circuits_iterator()) 4 - """ - if k: - for C in self._k_C[k]: - yield C + sage: list(M.circuits_iterator(0)) + [] + sage: sorted(M.circuits_iterator(3), key=str) + [frozenset({0, 1, 2}), + frozenset({0, 1, 3}), + frozenset({0, 2, 3}), + frozenset({1, 2, 3})] + """ + if k is not None: + if k in self._k_C: + for C in self._k_C[k]: + yield C else: for i in self._k_C: for C in self._k_C[i]: @@ -475,7 +539,7 @@ cdef class CircuitsMatroid(Matroid): cpdef nonspanning_circuits(self) noexcept: """ - Return the list of nonspanning circuits of the matroid. + Return the nonspanning circuits of the matroid. OUTPUT: a SetSystem @@ -486,13 +550,11 @@ cdef class CircuitsMatroid(Matroid): sage: M.nonspanning_circuits() Iterator over a system of subsets """ - cdef SetSystem NSC - NSC = SetSystem(list(self.groundset())) + cdef list NSC = [] for i in self._k_C: if i <= self.rank(): - for C in self._k_C[i]: - NSC.append(C) - return NSC + NSC.extend(self._k_C[i]) + return SetSystem(list(self.groundset()), NSC) def nonspanning_circuits_iterator(self): """ @@ -591,9 +653,7 @@ cdef class CircuitsMatroid(Matroid): sage: matroids.Theta(10).girth() 3 - REFERENCES: - - [Oxl2011]_, p. 327. + REFERENCES: [Oxl2011]_, p. 327. """ return min(self._k_C, default=float('inf')) @@ -655,16 +715,17 @@ cdef class CircuitsMatroid(Matroid): for j in self._k_C: if i <= j: for C1 in self._k_C[i]: - if len(C1) == 0: + if not C1: return False for C2 in self._k_C[j]: if C1 < C2: return False if C1 == C2: break + UC12 = set(C1) | set(C2) for e in C1 & C2: flag = False - S = (set(C1) | set(C2)) - {e} + S = UC12 - {e} for k in self._k_C: if k <= len(S) and not flag: for C3 in self._k_C[k]: diff --git a/src/sage/matroids/database_collections.py b/src/sage/matroids/database_collections.py index 7480df75e62..f7c67431b07 100644 --- a/src/sage/matroids/database_collections.py +++ b/src/sage/matroids/database_collections.py @@ -102,10 +102,8 @@ def AllMatroids(n, r=None, type="all"): AttributeError: The type "nice" is not available. There needs to be an "is_nice()" attribute for the type to be supported. - REFERENCES: - - The underlying database was retrieved from Yoshitake Matsumoto's Database - of Matroids; see [Mat2012]_. + REFERENCES: The underlying database was retrieved from Yoshitake + Matsumoto's Database of Matroids; see [Mat2012]_. TESTS:: @@ -248,10 +246,8 @@ def OxleyMatroids(): :mod:`Matroid catalog `, under ``Oxley's matroid collection``. - REFERENCES: - - These matroids are the nonparametrized matroids that appear in the - Appendix ``Some Interesting Matroids`` in [Oxl2011]_ (p. 639-64). + REFERENCES: These matroids are the nonparametrized matroids that appear in + the Appendix ``Some Interesting Matroids`` in [Oxl2011]_ (p. 639-64). """ from sage.matroids.database_matroids import ( U24, U25, U35, K4, Whirl3, Q6, P6, U36, R6, diff --git a/src/sage/matroids/database_matroids.py b/src/sage/matroids/database_matroids.py index 73ef805ea34..a420a50b8cd 100644 --- a/src/sage/matroids/database_matroids.py +++ b/src/sage/matroids/database_matroids.py @@ -93,9 +93,7 @@ def U24(): sage: M.is_3connected() True - REFERENCES: - - [Oxl2011]_, p. 639. + REFERENCES: [Oxl2011]_, p. 639. """ M = Uniform(2, 4) return M @@ -116,9 +114,7 @@ def U25(): sage: U25.is_isomorphic(U35.dual()) True - REFERENCES: - - [Oxl2011]_, p. 640. + REFERENCES: [Oxl2011]_, p. 640. """ M = Uniform(2, 5) return M @@ -139,9 +135,7 @@ def U35(): sage: U35.is_isomorphic(U25.dual()) True - REFERENCES: - - [Oxl2011]_, p. 640. + REFERENCES: [Oxl2011]_, p. 640. """ M = Uniform(3, 5) return M @@ -173,9 +167,7 @@ def K4(): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 640. + REFERENCES: [Oxl2011]_, p. 640. """ M = CompleteGraphic(4) return M @@ -198,8 +190,8 @@ def Whirl3(): sage: W.automorphism_group().is_transitive() False - For all elements `e`, neither `\mathcal{W}_3 \setminus \{e\}` nor - `\mathcal{W}_3 / \{e\}` is `3`-connected:: + For all elements `e`, neither `\mathcal{W}_3 \setminus \{e\}` nor `\mathcal{W}_3 / \{e\}` + is `3`-connected:: sage: import random sage: e = random.choice(list(W.groundset())) @@ -208,9 +200,7 @@ def Whirl3(): sage: W.contract(e).is_3connected() False - REFERENCES: - - [Oxl2011]_, p. 641. + REFERENCES: [Oxl2011]_, p. 641. """ M = Whirl(3) return M @@ -238,9 +228,7 @@ def Q6(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 641. + REFERENCES: [Oxl2011]_, p. 641. """ F = GF(4, 'x') x = F.gens()[0] @@ -266,17 +254,14 @@ def P6(): {2: {{'a', 'b', 'c'}}, 3: {{'a', 'b', 'c', 'd', 'e', 'f'}}} sage: len(set(M.nonspanning_circuits()).difference(M.nonbases())) == 0 True - sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, - ....: nrows=5)).has_minor(M) + sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, nrows=5)).has_minor(M) False sage: M.is_valid() True sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 641-2. + REFERENCES: [Oxl2011]_, p. 641-2. """ E = 'abcdef' CC = {2: ['abc'], 3: [E]} @@ -305,9 +290,7 @@ def U36(): sage: U36.automorphism_group().structure_description() 'S6' - REFERENCES: - - [Oxl2011]_, p. 642. + REFERENCES: [Oxl2011]_, p. 642. """ M = Uniform(3, 6) return M @@ -334,9 +317,7 @@ def R6(): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 642. + REFERENCES: [Oxl2011]_, p. 642. """ A = Matrix( GF(3), [[1, 0, 0, 1, 1, 1], [0, 1, 0, 1, 2, 1], [0, 0, 1, 1, 0, 2]] @@ -383,9 +364,7 @@ def Fano(): sage: M.is_isomorphic(matroids.Z(3)) True - REFERENCES: - - [Oxl2011]_, p. 643. + REFERENCES: [Oxl2011]_, p. 643. """ A = Matrix( GF(2), @@ -422,9 +401,7 @@ def FanoDual(): sage: F7D.delete(e).is_isomorphic(K2_3) True - REFERENCES: - - [Oxl2011]_, p. 643. + REFERENCES: [Oxl2011]_, p. 643. """ M = Fano().dual() M.rename("F7*: " + repr(M)) @@ -445,16 +422,14 @@ def NonFano(): sage: M = matroids.catalog.NonFano(); M NonFano: Ternary matroid of rank 3 on 7 elements, type 0- sage: setprint(M.nonbases()) - [{'a', 'b', 'f'}, {'a', 'c', 'e'}, {'a', 'd', 'g'}, {'b', 'c', 'd'}, - {'b', 'e', 'g'}, {'c', 'f', 'g'}] + [{'a', 'b', 'f'}, {'a', 'c', 'e'}, {'a', 'd', 'g'}, + {'b', 'c', 'd'}, {'b', 'e', 'g'}, {'c', 'f', 'g'}] sage: M.delete('f').is_isomorphic(matroids.CompleteGraphic(4)) True sage: M.delete('g').is_isomorphic(matroids.CompleteGraphic(4)) False - REFERENCES: - - [Oxl2011]_, p. 643-4. + REFERENCES: [Oxl2011]_, p. 643-4. """ A = Matrix( GF(3), @@ -479,8 +454,8 @@ def NonFanoDual(): sage: sorted(M.groundset()) ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - Every single-element contraction of `(F_7^-)^*` is isomorphic to `M(K_4)` - or `\mathcal{W}^3`:: + Every single-element contraction of `(F_7^-)^*` is isomorphic to `M(K_4)` or + `\mathcal{W}^3`:: sage: import random sage: e = random.choice(list(M.groundset())) @@ -490,9 +465,7 @@ def NonFanoDual(): sage: N.is_isomorphic(K4) or N.is_isomorphic(W3) True - REFERENCES: - - [Oxl2011]_, p. 643-4. + REFERENCES: [Oxl2011]_, p. 643-4. """ M = NonFano().dual() M.rename("NonFano*: " + repr(M)) @@ -516,9 +489,7 @@ def O7(): sage: M.tutte_polynomial() y^4 + x^3 + x*y^2 + 3*y^3 + 4*x^2 + 5*x*y + 5*y^2 + 4*x + 4*y - REFERENCES: - - [Oxl2011]_, p. 644. + REFERENCES: [Oxl2011]_, p. 644. """ A = Matrix( GF(3), @@ -549,9 +520,7 @@ def P7(): sage: M.is_valid() True - REFERENCES: - - [Oxl2011]_, p. 644-5. + REFERENCES: [Oxl2011]_, p. 644-5. """ A = Matrix( GF(3), @@ -585,8 +554,8 @@ def AG32(): sage: M.equals(M.dual()) True - Every single-element deletion is isomorphic to `F_7^*` and every - single-element contraction is isomorphic to `F_7`:: + Every single-element deletion is isomorphic to `F_7^*` and every single-element + contraction is isomorphic to `F_7`:: sage: F7 = matroids.catalog.Fano() sage: F7D = matroids.catalog.FanoDual() @@ -597,9 +566,7 @@ def AG32(): sage: M.contract(e).is_isomorphic(F7) True - REFERENCES: - - [Oxl2011]_, p. 645. + REFERENCES: [Oxl2011]_, p. 645. """ M = AG(3, 2) return M @@ -640,8 +607,8 @@ def AG32prime(): sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - Every single-element deletion is isomorphic to `F_7^*` or `(F_7^-)^*` and - every single-element contraction is isomorphic to `F_7` or `F_7^-`:: + Every single-element deletion is isomorphic to `F_7^*` or `(F_7^-)^*` and every + single-element contraction is isomorphic to `F_7` or `F_7^-`:: sage: F7 = matroids.catalog.Fano() sage: F7D = matroids.catalog.FanoDual() @@ -655,9 +622,7 @@ def AG32prime(): sage: Me.is_isomorphic(F7) or Me.is_isomorphic(F7m) True - REFERENCES: - - [Oxl2011]_, p. 646. + REFERENCES: [Oxl2011]_, p. 646. """ CC = { 3: [ @@ -705,9 +670,7 @@ def R8(): sage: M.contract(e).is_isomorphic(F7m) True - REFERENCES: - - [Oxl2011]_, p. 646. + REFERENCES: [Oxl2011]_, p. 646. """ A = Matrix( GF(3), @@ -740,8 +703,7 @@ def F8(): {'a', 'e', 'f', 'h'}, {'b', 'c', 'd', 'g'}, {'b', 'c', 'e', 'f'}, {'c', 'd', 'e', 'h'}, {'c', 'f', 'g', 'h'}, {'d', 'e', 'f', 'g'}}, 4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}} - sage: D = get_nonisomorphic_matroids([M.contract(i) - ....: for i in M.groundset()]) + sage: D = get_nonisomorphic_matroids([M.contract(i) for i in M.groundset()]) sage: len(D) 3 sage: [N.is_isomorphic(matroids.catalog.Fano()) for N in D] @@ -755,9 +717,7 @@ def F8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 647. + REFERENCES: [Oxl2011]_, p. 647. """ CC = { 3: [ @@ -805,9 +765,7 @@ def Q8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 647. + REFERENCES: [Oxl2011]_, p. 647. """ CC = { 3: [ @@ -857,9 +815,7 @@ def L8(): sage: M.contract(e).is_isomorphic(K4ext) True - REFERENCES: - - [Oxl2011]_, p. 648. + REFERENCES: [Oxl2011]_, p. 648. """ CC = {3: ['abfg', 'bcdg', 'defg', 'cdeh', 'aefh', 'abch', 'aceg', 'bdfh'], 4: ['abcdefgh']} @@ -891,8 +847,7 @@ def S8(): sage: M.is_graphic() False sage: D = get_nonisomorphic_matroids( - ....: list(matroids.catalog.Fano().linear_coextensions( - ....: cosimple=True))) + ....: list(matroids.catalog.Fano().linear_coextensions(cosimple=True))) sage: len(D) 2 sage: [N.is_isomorphic(M) for N in D] @@ -902,9 +857,7 @@ def S8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 648. + REFERENCES: [Oxl2011]_, p. 648. """ A = Matrix( GF(2), @@ -948,9 +901,7 @@ def Vamos(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 649. + REFERENCES: [Oxl2011]_, p. 649. """ CC = {3: ['abcd', 'abef', 'cdef', 'abgh', 'efgh'], 4: ['abcdefgh']} M = CircuitClosuresMatroid(groundset='abcdefgh', circuit_closures=CC) @@ -981,9 +932,7 @@ def T8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 649. + REFERENCES: [Oxl2011]_, p. 649. """ A = Matrix( GF(3), @@ -1022,9 +971,7 @@ def J(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 650. + REFERENCES: [Oxl2011]_, p. 650. """ A = Matrix( GF(3), @@ -1055,15 +1002,12 @@ def P8(): P8: Ternary matroid of rank 4 on 8 elements, type 2+ sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, - ....: nrows=5)).has_minor(M) + sage: Matroid(matrix=random_matrix(GF(4, 'a'), ncols=5, nrows=5)).has_minor(M) False sage: M.bicycle_dimension() 2 - REFERENCES: - - [Oxl2011]_, p. 650-1. + REFERENCES: [Oxl2011]_, p. 650-1. """ A = Matrix( GF(3), @@ -1099,15 +1043,12 @@ def P8pp(): 4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}} sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - sage: len(get_nonisomorphic_matroids([M.contract(i) - ....: for i in M.groundset()])) + sage: len(get_nonisomorphic_matroids([M.contract(i) for i in M.groundset()])) 1 sage: M.is_valid() # long time True - REFERENCES: - - [Oxl2011]_, p. 651. + REFERENCES: [Oxl2011]_, p. 651. """ CC = {3: ['abfh', 'bceg', 'cdfh', 'adeg', 'acef', 'bdfg', 'acgh', 'bdeh'], 4: ['abcdefgh']} @@ -1134,9 +1075,7 @@ def Wheel4(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 651-2. + REFERENCES: [Oxl2011]_, p. 651-2. """ M = Wheel(4) return M @@ -1160,9 +1099,7 @@ def Whirl4(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 652. + REFERENCES: [Oxl2011]_, p. 652. """ M = Whirl(4) return M @@ -1181,17 +1118,14 @@ def K33dual(): sage: M = matroids.catalog.K33dual(); M M*(K3, 3): Regular matroid of rank 4 on 9 elements with 81 bases - sage: any(N.is_3connected() - ....: for N in M.linear_extensions(simple=True)) + sage: any(N.is_3connected() for N in M.linear_extensions(simple=True)) False sage: M.is_valid() True sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 652-3. + REFERENCES: [Oxl2011]_, p. 652-3. """ from sage.graphs.graph_generators import graphs @@ -1219,9 +1153,7 @@ def K33(): sage: G1.is_isomorphic(G2) True - REFERENCES: - - [Oxl2011]_, p. 652-3. + REFERENCES: [Oxl2011]_, p. 652-3. """ from sage.graphs.graph_generators import graphs @@ -1253,9 +1185,7 @@ def AG23(): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 653. + REFERENCES: [Oxl2011]_, p. 653. """ M = AG(2, 3) return M @@ -1280,9 +1210,7 @@ def TernaryDowling3(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 654. + REFERENCES: [Oxl2011]_, p. 654. """ A = Matrix( GF(3), @@ -1308,7 +1236,7 @@ def R9(): EXAMPLES:: sage: M = matroids.catalog.R9(); M - R9: Matroid of rank 3 on 9 elements with 15 non-spanning circuits + R9: Matroid of rank 3 on 9 elements with 15 nonspanning circuits sage: M.is_valid() True sage: len(M.nonspanning_circuits()) @@ -1318,9 +1246,7 @@ def R9(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 654. + REFERENCES: [Oxl2011]_, p. 654. """ NSC = ['abc', 'abd', 'acd', 'aef', 'agh', 'bcd', 'bfh', 'bgi', 'ceg', 'cfi', 'deh', 'dei', 'dfg', 'dhi', 'ehi'] @@ -1358,9 +1284,7 @@ def Pappus(groundset=None): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 655. + REFERENCES: [Oxl2011]_, p. 655. """ CC = {2: ['abc', 'def', 'ceg', 'bfg', 'cdh', 'afh', 'bdi', 'aei', 'ghi'], 3: ['abcdefghi']} @@ -1396,9 +1320,7 @@ def NonPappus(): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 655. + REFERENCES: [Oxl2011]_, p. 655. """ CC = {2: ['abc', 'ceg', 'bfg', 'cdh', 'afh', 'bdi', 'aei', 'ghi'], 3: ['abcdefghi']} @@ -1423,9 +1345,7 @@ def K5(): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 656. + REFERENCES: [Oxl2011]_, p. 656. """ M = CompleteGraphic(5) return M @@ -1448,9 +1368,7 @@ def K5dual(): sage: G1.is_isomorphic(G2) True - REFERENCES: - - [Oxl2011]_, p. 656. + REFERENCES: [Oxl2011]_, p. 656. """ M = CompleteGraphic(5).dual() M.rename("M*(K5): " + repr(M)) @@ -1471,7 +1389,7 @@ def R10(): R10: Regular matroid of rank 5 on 10 elements with 162 bases sage: cct = [] sage: for i in M.circuits(): - ....: cct.append(len(i)) + ....: cct.append(len(i)) sage: Set(cct) {4, 6} sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) @@ -1498,9 +1416,7 @@ def R10(): sage: matroids.catalog.R10().linear_extensions(simple=True) [] - REFERENCES: - - [Oxl2011]_, p. 656-7. + REFERENCES: [Oxl2011]_, p. 656-7. """ A = Matrix( ZZ, @@ -1527,15 +1443,14 @@ def NonDesargues(groundset=None): EXAMPLES:: - sage: M = matroids.catalog.NonDesargues() + sage: M = matroids.catalog.NonDesargues(); M + NonDesargues: Matroid of rank 3 on 10 elements with 9 nonspanning circuits sage: M.is_valid() True sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 657. + REFERENCES: [Oxl2011]_, p. 657. """ NSC = ['acj', 'aef', 'bce', 'bfj', 'bgi', 'chi', 'dfg', 'dij', 'egh'] M = Matroid(rank=3, nonspanning_circuits=NSC) @@ -1563,9 +1478,7 @@ def R12(groundset='abcdefghijkl'): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 657. + REFERENCES: [Oxl2011]_, p. 657. """ A = Matrix( ZZ, @@ -1634,9 +1547,7 @@ def ExtendedTernaryGolayCode(): :class:`GolayCode ` - REFERENCES: - - [Oxl2011]_, p. 658. + REFERENCES: [Oxl2011]_, p. 658. """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 0], @@ -1672,9 +1583,7 @@ def T12(): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 658-9. + REFERENCES: [Oxl2011]_, p. 658-9. """ A = Matrix( GF(2), @@ -1708,9 +1617,7 @@ def PG23(): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 659. + REFERENCES: [Oxl2011]_, p. 659. """ M = PG(2, 3) return M @@ -1728,9 +1635,7 @@ def Wheel(r, field=None, ring=None): will be a regular matroid. - ``field`` -- any field; same as ``ring``, but only fields are allowed - OUTPUT: - - the rank-`r` wheel matroid, represented as a regular matroid + OUTPUT: the rank-`r` wheel matroid, represented as a regular matroid EXAMPLES:: @@ -1760,9 +1665,7 @@ def Wheel(r, field=None, ring=None): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 659-60. + REFERENCES: [Oxl2011]_, p. 659-60. """ base_ring = ZZ if field is not None and ring is not None: @@ -1802,9 +1705,7 @@ def Whirl(r): - ``r`` -- a positive integer; the rank of the desired matroid. - OUTPUT: - - the rank-`r` whirl matroid, represented as a ternary matroid + OUTPUT: the rank-`r` whirl matroid, represented as a ternary matroid EXAMPLES:: @@ -1813,8 +1714,8 @@ def Whirl(r): sage: M.is_valid() True sage: M.tutte_polynomial() - x^5 + y^5 + 5*x^4 + 5*x^3*y + 5*x^2*y^2 + 5*x*y^3 + 5*y^4 + 10*x^3 + - 15*x^2*y + 15*x*y^2 + 10*y^3 + 10*x^2 + 15*x*y + 10*y^2 + 5*x + 5*y + x^5 + y^5 + 5*x^4 + 5*x^3*y + 5*x^2*y^2 + 5*x*y^3 + 5*y^4 + 10*x^3 + 15*x^2*y + + 15*x*y^2 + 10*y^3 + 10*x^2 + 15*x*y + 10*y^2 + 5*x + 5*y sage: M.is_isomorphic(matroids.Wheel(5)) False sage: M = matroids.Whirl(3) @@ -1845,9 +1746,7 @@ def Whirl(r): ``[ 1 -1 0]`` ``[ 0 1 -1]`` - REFERENCES: - - [Oxl2011]_, p. 659-60. + REFERENCES: [Oxl2011]_, p. 659-60. """ A = Matrix(GF(3), r, 2 * r, sparse=True) for i in range(r): @@ -1876,9 +1775,7 @@ def Uniform(r, n): - ``n`` -- a nonnegative integer; the number of elements of the uniform matroid - OUTPUT: - - the uniform matroid `U_{r,n}` + OUTPUT: the uniform matroid `U_{r,n}` EXAMPLES:: @@ -1901,9 +1798,7 @@ def Uniform(r, n): sage: len(M.circuit_closures()) 0 - REFERENCES: - - [Oxl2011]_, p. 660. + REFERENCES: [Oxl2011]_, p. 660. """ E = range(n) if r < n: @@ -1930,9 +1825,7 @@ def PG(n, q, x=None): non-prime field, used for non-prime fields. If not supplied, ``'x'`` is used. - OUTPUT: - - a linear matroid whose elements are the points of `PG(n, q)` + OUTPUT: a linear matroid whose elements are the points of `PG(n, q)` EXAMPLES:: @@ -1942,12 +1835,10 @@ def PG(n, q, x=None): sage: matroids.PG(5, 4, 'z').size() == (4^6 - 1) / (4 - 1) True sage: M = matroids.PG(4, 7); M - PG(4, 7): Linear matroid of rank 5 on 2801 elements represented over - the Finite Field of size 7 - - REFERENCES: + PG(4, 7): Linear matroid of rank 5 on 2801 elements represented over the Finite Field + of size 7 - [Oxl2011]_, p. 660. + REFERENCES: [Oxl2011]_, p. 660. """ if x is None: x = 'x' @@ -1977,24 +1868,19 @@ def AG(n, q, x=None): non-prime field, used for non-prime fields. If not supplied, ``'x'`` is used. - OUTPUT: - - a linear matroid whose elements are the points of `AG(n, q)` + OUTPUT: a linear matroid whose elements are the points of `AG(n, q)` EXAMPLES:: sage: M = matroids.AG(2, 3).delete(8) sage: M.is_isomorphic(matroids.catalog.AG23minus()) True - sage: matroids.AG(5, 4, 'z').size() == ((4 ^ 6 - 1) / (4 - 1) - - ....: (4 ^ 5 - 1)/(4 - 1)) + sage: matroids.AG(5, 4, 'z').size() == ((4 ^ 6 - 1) / (4 - 1) - (4 ^ 5 - 1)/(4 - 1)) True sage: M = matroids.AG(4, 2); M AG(4, 2): Binary matroid of rank 5 on 16 elements, type (5, 0) - REFERENCES: - - [Oxl2011]_, p. 661. + REFERENCES: [Oxl2011]_, p. 661. """ if x is None: x = 'x' @@ -2019,9 +1905,7 @@ def Z(r, t=True): - ``r`` -- an integer (`r \ge 3`); the rank of the spike - ``t`` -- a Boolean (default: ``True``); whether the spike is tipped - OUTPUT: - - a matroid; the unique rank-`r` binary spike (tipped or tipless) + OUTPUT: a matroid; the unique rank-`r` binary spike (tipped or tipless) EXAMPLES:: @@ -2064,9 +1948,7 @@ def Z(r, t=True): sage: Z.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 661-2. + REFERENCES: [Oxl2011]_, p. 661-2. """ from sage.matrix.special import identity_matrix, ones_matrix Id = Matrix(GF(2), identity_matrix(r)) @@ -2096,7 +1978,7 @@ def Spike(r, t=True, C3=[]): The groundset is `E = \{t, x_1, x_2, \ldots, x_r, y_1, y_2, \ldots, y_r\}` with `r(E) = r`. - The non-spanning circuits are `\{L_1, L_2, \ldots, L_r\}`, all sets of the + The nonspanning circuits are `\{L_1, L_2, \ldots, L_r\}`, all sets of the form `(L_i \cup L_j) \setminus t` for `1 \le i < j \le r`, and some (possibly empty) collection `C_3` of sets of the form `\{z_1, z_2, \ldots, z_r\}` where `z_i \in \{x_i, y_i\}` for all `i`, and no two members of @@ -2109,15 +1991,13 @@ def Spike(r, t=True, C3=[]): - ``C3`` -- a list (default: ``[]``); a list of extra nonspanning circuits. The default (i.e. the empty list) results in a free `r`-spike - OUTPUT: - - a matroid; a rank-`r` spike (tipped or tipless) + OUTPUT: a matroid; a rank-`r` spike (tipped or tipless) EXAMPLES:: sage: M = matroids.Spike(3, False); M - Free 3-spike\t: M \ {'t'}, where M is Matroid of rank 3 on 7 elements - with 3 non-spanning circuits + Free 3-spike\t: M \ {'t'}, where M is Matroid of rank 3 on 7 elements with 3 + nonspanning circuits sage: M.is_isomorphic(matroids.Uniform(3, 6)) True sage: len(matroids.Spike(8).bases()) @@ -2162,9 +2042,7 @@ def Spike(r, t=True, C3=[]): sage: M.equals(M.dual()) True - REFERENCES: - - [Oxl2011]_, p. 662. + REFERENCES: [Oxl2011]_, p. 662. """ if not (r >= 3): raise ValueError("The r-spike is defined for r >= 3.") @@ -2228,9 +2106,7 @@ def Theta(n): - ``n`` -- an integer (`n \ge 2`); the rank of the matroid - OUTPUT: - - a matroid (`\Theta_n`) + OUTPUT: a matroid (`\Theta_n`) EXAMPLES:: @@ -2254,8 +2130,7 @@ def Theta(n): sage: M.is_isomorphic(M.dual()) and not M.equals(M.dual()) True - For `n \le 3`, its automorphism group is transitive, while for `n \ge 4` - it is not:: + For `n \le 3`, its automorphism group is transitive, while for `n \ge 4` it is not:: sage: n = random.choice(range(4, 8)) sage: M = matroids.Theta(2 + n % 2) @@ -2265,9 +2140,7 @@ def Theta(n): sage: M.automorphism_group().is_transitive() False - REFERENCES: - - [Oxl2011]_, p. 663-4. + REFERENCES: [Oxl2011]_, p. 663-4. """ X = ['x'+str(i) for i in range(n)] Y = ['y'+str(i) for i in range(n)] @@ -2302,17 +2175,14 @@ def Psi(r): - ``r`` -- an integer (`r \ge 3`); the rank of the matroid - OUTPUT: - - a matroid (`\Psi_r`) + OUTPUT: a matroid (`\Psi_r`) EXAMPLES:: sage: matroids.Psi(7) Psi_7: Matroid of rank 7 on 14 elements with 2060 bases - The matroid `\Psi_r` is `3`-connected but, for all `r \ge 4`, not - `4`-connected:: + The matroid `\Psi_r` is `3`-connected but, for all `r \ge 4`, not `4`-connected:: sage: M = matroids.Psi(3) sage: M.is_4connected() @@ -2337,9 +2207,7 @@ def Psi(r): sage: M.automorphism_group().is_transitive() True - REFERENCES: - - [Oxl2011]_, p. 664. + REFERENCES: [Oxl2011]_, p. 664. """ A = ['a'+str(i) for i in range(0, r)] B = ['b'+str(i) for i in range(0, r)] @@ -2848,8 +2716,7 @@ def BB9gDY(): EXAMPLES:: sage: M = matroids.catalog.BB9gDY(); M - Segment cosegment exchange on BB9: Quaternary matroid of rank 5 on 9 - elements + Segment cosegment exchange on BB9: Quaternary matroid of rank 5 on 9 elements sage: M.is_valid() True """ @@ -4663,9 +4530,7 @@ def NonVamos(): sage: M.is_valid() # long time True - REFERENCES: - - [Oxl2011]_, p. 72, 84. + REFERENCES: [Oxl2011]_, p. 72, 84. """ E = 'abcdefgh' CC = { @@ -4691,9 +4556,7 @@ def NotP8(): sage: M.is_valid() True - REFERENCES: - - [Oxl1992]_, p.512 (the first edition). + REFERENCES: [Oxl1992]_, p.512 (the first edition). """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 1, 1, -1], @@ -4724,9 +4587,7 @@ class of near-regular matroids. sage: M.is_valid() True - REFERENCES: - - [Oxl2011]_, p. 653. + REFERENCES: [Oxl2011]_, p. 653. """ E = 'abcdefgh' CC = {2: ['abc', 'ceh', 'fgh', 'adf', 'aeg', 'cdg', 'bdh', 'bef'], @@ -4747,10 +4608,8 @@ def P9(): sage: M.is_valid() True - REFERENCES: - - This is the matroid referred to as `P_9` by Oxley in his paper "The binary - matroids with no 4-wheel minor", [Oxl1987]_. + REFERENCES: This is the matroid referred to as `P_9` by Oxley in his paper + "The binary matroids with no 4-wheel minor", [Oxl1987]_. """ A = Matrix(GF(2), [ [1, 0, 0, 0, 1, 0, 0, 1, 1], @@ -4813,7 +4672,7 @@ def R9B(): def Block_9_4(): """ - Return the paving matroid whose non-spanning circuits form the blocks of a + Return the paving matroid whose nonspanning circuits form the blocks of a `2-(9, 4, 3)` design. EXAMPLES:: @@ -4850,9 +4709,7 @@ def TicTacToe(): sage: M.is_valid() # long time True - REFERENCES: - - [Hoc]_ + REFERENCES: [Hoc]_ """ E = 'abcdefghi' CC = { @@ -4880,9 +4737,7 @@ def N1(): sage: M.is_valid() True - REFERENCES: - - [Oxl2011]_, p. 554. + REFERENCES: [Oxl2011]_, p. 554. """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 2, 0, 0, 1, 1], @@ -4898,7 +4753,7 @@ def N1(): def Block_10_5(): """ - Return the paving matroid whose non-spanning circuits form the blocks of a + Return the paving matroid whose nonspanning circuits form the blocks of a `3-(10, 5, 3)` design. EXAMPLES:: @@ -5008,9 +4863,7 @@ def N2(): sage: M.is_valid() True - REFERENCES: - - [Oxl2011]_, p. 554. + REFERENCES: [Oxl2011]_, p. 554. """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1], @@ -5041,9 +4894,7 @@ def D16(groundset='abcdefghijklmnop'): # A.K.A. the Carolyn Chun Matroid sage: M.is_valid() True - REFERENCES: - - [CMO2012]_ + REFERENCES: [CMO2012]_ """ A = Matrix(GF(2), [ [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0], @@ -5074,9 +4925,7 @@ def Terrahawk(): # A.K.A. the Dillon Mayhew Matroid sage: M.is_valid() True - REFERENCES: - - [CMO2011]_ + REFERENCES: [CMO2011]_ """ A = Matrix(GF(2), [ [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -5100,8 +4949,7 @@ def ExtendedBinaryGolayCode(): EXAMPLES:: sage: M = matroids.catalog.ExtendedBinaryGolayCode(); M - Extended Binary Golay Code: Binary matroid of rank 12 on 24 elements, - type (12, 0) + Extended Binary Golay Code: Binary matroid of rank 12 on 24 elements, type (12, 0) sage: C = LinearCode(M.representation()) sage: C.is_permutation_equivalent(codes.GolayCode(GF(2))) True @@ -5152,9 +5000,7 @@ def CompleteGraphic(n): - ``n`` -- an integer, the number of vertices of the underlying complete graph. - OUTPUT: - - The graphic matroid associated with the `n`-vertex complete graph. + OUTPUT: The graphic matroid associated with the `n`-vertex complete graph. This matroid has rank `n - 1`. EXAMPLES:: diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 9bd50d2053b..fc1f5dd9cc2 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -3124,9 +3124,7 @@ cdef class Matroid(SageObject): A 6-dimensional polyhedron in ZZ^7 defined as the convex hull of 29 vertices - REFERENCE: - - - [DLHK2007]_ + REFERENCES: [DLHK2007]_ """ from sage.geometry.polyhedron.constructor import Polyhedron from sage.modules.free_module import FreeModule @@ -3167,9 +3165,7 @@ cdef class Matroid(SageObject): A 7-dimensional polyhedron in ZZ^7 defined as the convex hull of 58 vertices - REFERENCE: - - [DLHK2007]_ + REFERENCES: [DLHK2007]_ """ from sage.geometry.polyhedron.constructor import Polyhedron from sage.modules.free_module import FreeModule @@ -5996,9 +5992,7 @@ cdef class Matroid(SageObject): sage: matroids.catalog.Vamos().girth() 4 - REFERENCES: - - [Oxl2011]_, p. 327. + REFERENCES: [Oxl2011]_, p. 327. """ for k in range(self.rank() + 2): for X in combinations(self.groundset(), k): @@ -7992,9 +7986,7 @@ cdef class Matroid(SageObject): sage: G.structure_description() 'M12' - REFERENCES: - - [Oxl2011]_, p. 189. + REFERENCES: [Oxl2011]_, p. 189. """ from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(self.bases()).automorphism_group() From f63109b2d103ff211dcc4df9cc28ef2e550b1d70 Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 7 Feb 2024 17:49:06 +0200 Subject: [PATCH 157/191] Less checks and indents in circuits is_valid() --- src/sage/matroids/circuits_matroid.pyx | 51 +++++++++++++------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index 5ab01ab74b3..f0ddc97eaec 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -230,10 +230,10 @@ cdef class CircuitsMatroid(Matroid): INPUT: - ``other`` -- a matroid - - ``certificate`` -- boolean (optional) + - ``certificate`` -- boolean (default: ``False``) - OUTPUT: boolean, and, if certificate = True, a dictionary giving the - isomorphism or None + OUTPUT: boolean, and, if ``certificate=True``, a dictionary giving the + isomorphism or ``None`` EXAMPLES:: @@ -711,27 +711,28 @@ cdef class CircuitsMatroid(Matroid): sage: M.is_valid() False """ - for i in self._k_C: - for j in self._k_C: - if i <= j: - for C1 in self._k_C[i]: - if not C1: + from itertools import combinations_with_replacement + for (i, j) in combinations_with_replacement(sorted(self._k_C), 2): + # loop through all circuit length pairs (i, j) with i <= j + for C1 in self._k_C[i]: + if not C1: # the empty set can't be a circuit + return False + for C2 in self._k_C[j]: + if C1 < C2: # a circuit can't be a subset of another circuit + return False + if C1 == C2: + break + # check circuit elimination axiom + UC12 = set(C1) | set(C2) + for e in C1 & C2: + flag = False + S = UC12 - {e} + for k in self._k_C: + if k <= len(S) and not flag: + for C3 in self._k_C[k]: + if C3 <= S: + flag = True + break + if not flag: return False - for C2 in self._k_C[j]: - if C1 < C2: - return False - if C1 == C2: - break - UC12 = set(C1) | set(C2) - for e in C1 & C2: - flag = False - S = UC12 - {e} - for k in self._k_C: - if k <= len(S) and not flag: - for C3 in self._k_C[k]: - if C3 <= S: - flag = True - break - if not flag: - return False return True From 411bb556a598c04e97e8b2e4da82425db1520ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Feb 2024 13:23:11 +0100 Subject: [PATCH 158/191] less usage of isinstance of old-style parent-like classes --- src/sage/algebras/algebra.py | 7 ++----- src/sage/algebras/all.py | 12 ++++++------ src/sage/categories/algebras.py | 5 +---- src/sage/matrix/matrix0.pyx | 8 ++++---- src/sage/modular/abvar/homology.py | 4 ++-- src/sage/modular/hecke/module.py | 4 ++-- src/sage/modular/modform/constructor.py | 6 +++--- src/sage/modular/modsym/modsym.py | 7 ++++--- src/sage/modular/quatalg/brandt.py | 4 ++-- src/sage/rings/commutative_algebra.py | 5 +++-- .../rings/polynomial/skew_polynomial_ring.py | 19 ++++++++----------- .../hyperelliptic_curves/monsky_washnitzer.py | 8 +++----- src/sage/schemes/plane_conics/constructor.py | 10 +++++----- src/sage/structure/element.pyx | 6 +++--- 14 files changed, 48 insertions(+), 57 deletions(-) diff --git a/src/sage/algebras/algebra.py b/src/sage/algebras/algebra.py index e69ea7da530..a63aae4e217 100644 --- a/src/sage/algebras/algebra.py +++ b/src/sage/algebras/algebra.py @@ -18,9 +18,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.ring import Algebra from sage.categories.algebras import Algebras + def is_Algebra(x): r""" Return True if x is an Algebra. @@ -37,7 +37,4 @@ def is_Algebra(x): """ from sage.misc.superseded import deprecation deprecation(35253, "the function is_Algebra is deprecated; use '... in Algebras(base_ring)' instead") - try: - return isinstance(x, Algebra) or x in Algebras(x.base_ring()) - except Exception: - return False + return x in Algebras(x.base_ring()) diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index 14be60e8f56..a98f5284eca 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -1,8 +1,7 @@ """ Algebras """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) @@ -14,10 +13,13 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.lazy_import import lazy_import +# old-style class for associative algebras, use Parent instead +from sage.rings.ring import Algebra + import sage.algebras.catalog as algebras from .quatalg.all import * @@ -28,11 +30,9 @@ from .lie_conformal_algebras.all import * # Algebra base classes -from .algebra import Algebra from .free_algebra import FreeAlgebra from .free_algebra_quotient import FreeAlgebraQuotient - from .finite_dimensional_algebras.all import FiniteDimensionalAlgebra lazy_import('sage.algebras.group_algebra', 'GroupAlgebra') diff --git a/src/sage/categories/algebras.py b/src/sage/categories/algebras.py index d93a51d28bd..5f7dcaff05b 100644 --- a/src/sage/categories/algebras.py +++ b/src/sage/categories/algebras.py @@ -73,10 +73,7 @@ def __contains__(self, x): sage: QQ['x'] in Algebras(CDF) # needs sage.rings.complex_double False """ - if super().__contains__(x): - return True - from sage.rings.ring import Algebra - return isinstance(x, Algebra) and x.base_ring() == self.base_ring() + return super().__contains__(x) # def extra_super_categories(self): # """ diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index dc642bf0f4e..d6c1d911067 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -39,9 +39,9 @@ from sage.misc.misc_c cimport normalize_index from sage.categories.fields import Fields from sage.categories.integral_domains import IntegralDomains +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.rings import Rings -from sage.rings.ring cimport CommutativeRing -from sage.rings.ring import is_Ring import sage.rings.abc from sage.rings.integer_ring import is_IntegerRing @@ -1660,7 +1660,7 @@ cdef class Matrix(sage.structure.element.Matrix): [---] [2 4] """ - if not is_Ring(ring): + if ring not in Rings(): raise TypeError("ring must be a ring") if ring is self._base_ring: @@ -5323,7 +5323,7 @@ cdef class Matrix(sage.structure.element.Matrix): [ -x*y*x*y x*y*x + x*y^2 x*y*x - x*y^2] """ # derived classes over a commutative base *just* overload _lmul_ (!!) - if isinstance(self._base_ring, CommutativeRing): + if self._base_ring in CommutativeRings(): return self._lmul_(left) cdef Py_ssize_t r,c x = self._base_ring(left) diff --git a/src/sage/modular/abvar/homology.py b/src/sage/modular/abvar/homology.py index 59cc17a77e7..5feb7e3cbce 100644 --- a/src/sage/modular/abvar/homology.py +++ b/src/sage/modular/abvar/homology.py @@ -56,7 +56,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal # TODO: we will probably also need homology that is *not* a Hecke module. @@ -117,7 +117,7 @@ def __init__(self, abvar, base): sage: loads(dumps(H)) == H True """ - if not isinstance(base, CommutativeRing): + if base not in CommutativeRings(): raise TypeError("base ring must be a commutative ring") HeckeModule_free_module.__init__( self, base, abvar.level(), weight=2) diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index d0d8d5e49d7..413064c233a 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -22,7 +22,7 @@ from sage.modules.module import Module from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.structure.sequence import Sequence from . import algebra @@ -76,7 +76,7 @@ def __init__(self, base_ring, level, category=None): sage: ModularForms(3, 3).category() Category of Hecke modules over Rational Field """ - if not isinstance(base_ring, CommutativeRing): + if base_ring not in CommutativeRings(): raise TypeError("base_ring must be commutative ring") from sage.categories.hecke_modules import HeckeModules diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index 2adde8bafc3..6465977b85e 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -37,7 +37,7 @@ import sage.modular.dirichlet as dirichlet from sage.rings.integer import Integer from sage.rings.rational_field import Q as QQ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from .ambient_eps import ModularFormsAmbient_eps from .ambient_g0 import ModularFormsAmbient_g0_Q @@ -122,11 +122,11 @@ def canonical_parameters(group, level, weight, base_ring): except TypeError: raise TypeError("group of unknown type.") level = Integer(level) - if ( m != level ): + if m != level: raise ValueError("group and level do not match.") group = arithgroup.Gamma0(m) - if not isinstance(base_ring, CommutativeRing): + if base_ring not in CommutativeRings(): raise TypeError("base_ring (=%s) must be a commutative ring" % base_ring) # it is *very* important to include the level as part of the data diff --git a/src/sage/modular/modsym/modsym.py b/src/sage/modular/modsym/modsym.py index 962e3c168ed..346500b8b86 100644 --- a/src/sage/modular/modsym/modsym.py +++ b/src/sage/modular/modsym/modsym.py @@ -106,10 +106,11 @@ import weakref +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.fields import Fields import sage.modular.arithgroup.all as arithgroup import sage.modular.dirichlet as dirichlet from sage.rings.integer import Integer -from sage.rings.ring import CommutativeRing from sage.rings.rational_field import RationalField @@ -153,10 +154,10 @@ def canonical_parameters(group, weight, sign, base_ring): if base_ring is None: base_ring = RationalField() - if not isinstance(base_ring, CommutativeRing): + elif base_ring not in CommutativeRings(): raise TypeError(f"base_ring (={base_ring}) must be a commutative ring") - if not base_ring.is_field(): + elif base_ring not in Fields(): raise TypeError(f"(currently) base_ring (={base_ring}) must be a field") return group, weight, sign, base_ring diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 1c74d953f6d..30d70254db6 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -203,6 +203,7 @@ # **************************************************************************** from sage.arith.misc import gcd, factor, prime_divisors, kronecker, next_prime +from sage.categories.commutative_rings import CommutativeRings from sage.matrix.constructor import matrix from sage.matrix.matrix_space import MatrixSpace from sage.misc.cachefunc import cached_method @@ -219,7 +220,6 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.power_series_ring import PowerSeriesRing from sage.rings.rational_field import QQ -from sage.rings.ring import CommutativeRing from sage.structure.richcmp import richcmp, richcmp_method lazy_import('sage.algebras.quatalg.quaternion_algebra', ['QuaternionAlgebra', 'basis_for_quaternion_lattice']) @@ -300,7 +300,7 @@ def BrandtModule(N, M=1, weight=2, base_ring=QQ, use_cache=True): raise ValueError("M must be coprime to N") if weight < 2: raise ValueError("weight must be at least 2") - if not isinstance(base_ring, CommutativeRing): + if base_ring not in CommutativeRings(): raise TypeError("base_ring must be a commutative ring") key = (N, M, weight, base_ring) if use_cache: diff --git a/src/sage/rings/commutative_algebra.py b/src/sage/rings/commutative_algebra.py index 92d734cce77..7f6b2ba5dfa 100644 --- a/src/sage/rings/commutative_algebra.py +++ b/src/sage/rings/commutative_algebra.py @@ -17,7 +17,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.ring import CommutativeAlgebra +from sage.categories.commutative_algebras import CommutativeAlgebras + def is_CommutativeAlgebra(x): """ @@ -35,4 +36,4 @@ def is_CommutativeAlgebra(x): """ from sage.misc.superseded import deprecation deprecation(35253, "the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead") - return isinstance(x, CommutativeAlgebra) + return x in CommutativeAlgebras(x.base_ring()) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 9d3ea67ee46..db5285ff66f 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -46,7 +46,7 @@ from sage.structure.richcmp import op_EQ, op_NE from sage.structure.category_object import normalize_names -from sage.rings.ring import Field +from sage.categories.fields import Fields from sage.matrix.matrix_space import MatrixSpace from sage.rings.morphism import RingHomomorphism @@ -85,17 +85,14 @@ def _base_ring_to_fraction_field(S): Ore Polynomial Ring in x over Fraction Field of Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 """ R = S.base_ring() - if isinstance(R, Field): + if R in Fields(): return S - else: - Q = R.fraction_field() - gens = R.gens() - sigmaS = S.twisting_morphism() - # try: - sigmaQ = Q.hom([Q(sigmaS(g)) for g in gens]) - return Q[S.variable_name(), sigmaQ] - # except Exception, e: - # raise ValueError("unable to lift the twisting morphism to a twisting morphism over %s (error was: %s)" % (Q, e)) + + Q = R.fraction_field() + gens = R.gens() + sigmaS = S.twisting_morphism() + sigmaQ = Q.hom([Q(sigmaS(g)) for g in gens]) + return Q[S.variable_name(), sigmaQ] def _minimal_vanishing_polynomial(R, eval_pts): diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index 2519cdf5117..03b795a4704 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -68,7 +68,7 @@ from sage.rings.laurent_series_ring import LaurentSeriesRing from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ -from sage.rings.ring import IntegralDomain +from sage.categories.integral_domains import IntegralDomains from sage.rings.infinity import Infinity from sage.rings.laurent_series_ring import is_LaurentSeriesRing from sage.rings.padics.factory import Qp as pAdicField @@ -3837,13 +3837,11 @@ def helper_matrix(self): pass # The smallest y term of (1/j) d(x^i y^j) is constant for all j. - L = [] x, y = self.base_ring().gens() n = self.degree() - for i in range(n): - L.append((y*x**i).diff().extract_pow_y(0)) + L = [(y * x**i).diff().extract_pow_y(0) for i in range(n)] A = matrix(L).transpose() - if not isinstance(A.base_ring(), IntegralDomain): + if A.base_ring() not in IntegralDomains(): # must be using integer_mod or something to approximate self._helper_matrix = (~A.change_ring(QQ)).change_ring(A.base_ring()) else: diff --git a/src/sage/schemes/plane_conics/constructor.py b/src/sage/schemes/plane_conics/constructor.py index 7bb681a4268..a078bc07d22 100644 --- a/src/sage/schemes/plane_conics/constructor.py +++ b/src/sage/schemes/plane_conics/constructor.py @@ -26,7 +26,7 @@ from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector -from sage.rings.ring import IntegralDomain +from sage.categories.integral_domains import IntegralDomains from sage.rings.rational_field import is_RationalField from sage.rings.finite_rings.finite_field_base import FiniteField from sage.rings.fraction_field import is_FractionField @@ -143,7 +143,7 @@ def Conic(base_field, F=None, names=None, unique=True): sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ - if not (base_field is None or isinstance(base_field, IntegralDomain)): + if not (base_field is None or base_field in IntegralDomains()): if names is None: names = F F = base_field @@ -173,7 +173,7 @@ def Conic(base_field, F=None, names=None, unique=True): if len(C) != 3: raise TypeError("points in F (=%s) must be planar" % F) P = C.universe() - if not isinstance(P, IntegralDomain): + if P not in IntegralDomains(): raise TypeError("coordinates of points in F (=%s) must " "be in an integral domain" % F) L.append(Sequence([C[0]**2, C[0] * C[1], @@ -217,8 +217,8 @@ def Conic(base_field, F=None, names=None, unique=True): if base_field is None: base_field = F.base_ring() - if not isinstance(base_field, IntegralDomain): - raise ValueError("Base field (=%s) must be a field" % base_field) + if base_field not in IntegralDomains(): + raise ValueError(f"Base field (={base_field}) must be a field") base_field = base_field.fraction_field() if names is None: names = F.parent().variable_names() diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index f1cf9d26ac7..29a92718916 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -3344,12 +3344,12 @@ cdef class CommutativeRingElement(RingElement): #This code is very general, it works for all integral domains that have the #is_square(root = True) option - from sage.rings.ring import IntegralDomain + from sage.categories.integral_domains import IntegralDomains P = self._parent is_sqr, sq_rt = self.is_square(root=True) if is_sqr: if all: - if not isinstance(P, IntegralDomain): + if P not in IntegralDomains(): raise NotImplementedError('sqrt() with all=True is only implemented for integral domains, not for %s' % P) if P.characteristic()==2 or sq_rt==0: #0 has only one square root, and in characteristic 2 everything also has only 1 root @@ -3357,7 +3357,7 @@ cdef class CommutativeRingElement(RingElement): return [ sq_rt, -sq_rt ] return sq_rt #from now on we know that self is not a square - if not isinstance(P, IntegralDomain): + if P not in IntegralDomains(): raise NotImplementedError('sqrt() of non squares is only implemented for integral domains, not for %s' % P) if not extend: #all square roots of a non-square should be an empty list From 32c07c8ab16703c3de306873688057fae0a979e4 Mon Sep 17 00:00:00 2001 From: Aurel Page Date: Wed, 7 Feb 2024 21:03:14 +0100 Subject: [PATCH 159/191] fix bug number in doc --- src/sage/rings/number_field/unit_group.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index a86d4236517..0aa4e396596 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -305,7 +305,8 @@ def __init__(self, number_field, proof=True, S=None): sage: tuple(US(K(u)) for u in US.gens()) == US.gens() True - Bug #36386 (pari stack overflow while expanding units) + Bug :issue:`36386` (pari stack overflow while expanding units):: + sage: d = 12936642 sage: K = QuadraticField(d) sage: K.unit_group(proof=False) From 2d18429024338a7df8879fefb0ebc2c84d5e1327 Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Thu, 8 Feb 2024 00:56:06 +0200 Subject: [PATCH 160/191] Revert formatting of references --- src/sage/matroids/circuits_matroid.pyx | 4 +- src/sage/matroids/database_collections.py | 12 +- src/sage/matroids/database_matroids.py | 250 ++++++++++++++++------ src/sage/matroids/matroid.pyx | 16 +- 4 files changed, 210 insertions(+), 72 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index f0ddc97eaec..4cf16b6ee32 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -653,7 +653,9 @@ cdef class CircuitsMatroid(Matroid): sage: matroids.Theta(10).girth() 3 - REFERENCES: [Oxl2011]_, p. 327. + REFERENCES: + + [Oxl2011]_, p. 327. """ return min(self._k_C, default=float('inf')) diff --git a/src/sage/matroids/database_collections.py b/src/sage/matroids/database_collections.py index f7c67431b07..c3276be61d6 100644 --- a/src/sage/matroids/database_collections.py +++ b/src/sage/matroids/database_collections.py @@ -102,8 +102,10 @@ def AllMatroids(n, r=None, type="all"): AttributeError: The type "nice" is not available. There needs to be an "is_nice()" attribute for the type to be supported. - REFERENCES: The underlying database was retrieved from Yoshitake - Matsumoto's Database of Matroids; see [Mat2012]_. + REFERENCES: + + The underlying database was retrieved from Yoshitake Matsumoto's Database + of Matroids; see [Mat2012]_. TESTS:: @@ -246,8 +248,10 @@ def OxleyMatroids(): :mod:`Matroid catalog `, under ``Oxley's matroid collection``. - REFERENCES: These matroids are the nonparametrized matroids that appear in - the Appendix ``Some Interesting Matroids`` in [Oxl2011]_ (p. 639-64). + REFERENCES: + + These matroids are the nonparametrized matroids that appear in the Appendix + ``Some Interesting Matroids`` in [Oxl2011]_ (p. 639-64). """ from sage.matroids.database_matroids import ( U24, U25, U35, K4, Whirl3, Q6, P6, U36, R6, diff --git a/src/sage/matroids/database_matroids.py b/src/sage/matroids/database_matroids.py index a420a50b8cd..0a75ce22283 100644 --- a/src/sage/matroids/database_matroids.py +++ b/src/sage/matroids/database_matroids.py @@ -93,7 +93,9 @@ def U24(): sage: M.is_3connected() True - REFERENCES: [Oxl2011]_, p. 639. + REFERENCES: + + [Oxl2011]_, p. 639. """ M = Uniform(2, 4) return M @@ -114,7 +116,9 @@ def U25(): sage: U25.is_isomorphic(U35.dual()) True - REFERENCES: [Oxl2011]_, p. 640. + REFERENCES: + + [Oxl2011]_, p. 640. """ M = Uniform(2, 5) return M @@ -135,7 +139,9 @@ def U35(): sage: U35.is_isomorphic(U25.dual()) True - REFERENCES: [Oxl2011]_, p. 640. + REFERENCES: + + [Oxl2011]_, p. 640. """ M = Uniform(3, 5) return M @@ -167,7 +173,9 @@ def K4(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 640. + REFERENCES: + + [Oxl2011]_, p. 640. """ M = CompleteGraphic(4) return M @@ -200,7 +208,9 @@ def Whirl3(): sage: W.contract(e).is_3connected() False - REFERENCES: [Oxl2011]_, p. 641. + REFERENCES: + + [Oxl2011]_, p. 641. """ M = Whirl(3) return M @@ -228,7 +238,9 @@ def Q6(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 641. + REFERENCES: + + [Oxl2011]_, p. 641. """ F = GF(4, 'x') x = F.gens()[0] @@ -261,7 +273,9 @@ def P6(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 641-2. + REFERENCES: + + [Oxl2011]_, p. 641-2. """ E = 'abcdef' CC = {2: ['abc'], 3: [E]} @@ -290,7 +304,9 @@ def U36(): sage: U36.automorphism_group().structure_description() 'S6' - REFERENCES: [Oxl2011]_, p. 642. + REFERENCES: + + [Oxl2011]_, p. 642. """ M = Uniform(3, 6) return M @@ -317,7 +333,9 @@ def R6(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 642. + REFERENCES: + + [Oxl2011]_, p. 642. """ A = Matrix( GF(3), [[1, 0, 0, 1, 1, 1], [0, 1, 0, 1, 2, 1], [0, 0, 1, 1, 0, 2]] @@ -364,7 +382,9 @@ def Fano(): sage: M.is_isomorphic(matroids.Z(3)) True - REFERENCES: [Oxl2011]_, p. 643. + REFERENCES: + + [Oxl2011]_, p. 643. """ A = Matrix( GF(2), @@ -401,7 +421,9 @@ def FanoDual(): sage: F7D.delete(e).is_isomorphic(K2_3) True - REFERENCES: [Oxl2011]_, p. 643. + REFERENCES: + + [Oxl2011]_, p. 643. """ M = Fano().dual() M.rename("F7*: " + repr(M)) @@ -429,7 +451,9 @@ def NonFano(): sage: M.delete('g').is_isomorphic(matroids.CompleteGraphic(4)) False - REFERENCES: [Oxl2011]_, p. 643-4. + REFERENCES: + + [Oxl2011]_, p. 643-4. """ A = Matrix( GF(3), @@ -465,7 +489,9 @@ def NonFanoDual(): sage: N.is_isomorphic(K4) or N.is_isomorphic(W3) True - REFERENCES: [Oxl2011]_, p. 643-4. + REFERENCES: + + [Oxl2011]_, p. 643-4. """ M = NonFano().dual() M.rename("NonFano*: " + repr(M)) @@ -489,7 +515,9 @@ def O7(): sage: M.tutte_polynomial() y^4 + x^3 + x*y^2 + 3*y^3 + 4*x^2 + 5*x*y + 5*y^2 + 4*x + 4*y - REFERENCES: [Oxl2011]_, p. 644. + REFERENCES: + + [Oxl2011]_, p. 644. """ A = Matrix( GF(3), @@ -520,7 +548,9 @@ def P7(): sage: M.is_valid() True - REFERENCES: [Oxl2011]_, p. 644-5. + REFERENCES: + + [Oxl2011]_, p. 644-5. """ A = Matrix( GF(3), @@ -566,7 +596,9 @@ def AG32(): sage: M.contract(e).is_isomorphic(F7) True - REFERENCES: [Oxl2011]_, p. 645. + REFERENCES: + + [Oxl2011]_, p. 645. """ M = AG(3, 2) return M @@ -622,7 +654,9 @@ def AG32prime(): sage: Me.is_isomorphic(F7) or Me.is_isomorphic(F7m) True - REFERENCES: [Oxl2011]_, p. 646. + REFERENCES: + + [Oxl2011]_, p. 646. """ CC = { 3: [ @@ -670,7 +704,9 @@ def R8(): sage: M.contract(e).is_isomorphic(F7m) True - REFERENCES: [Oxl2011]_, p. 646. + REFERENCES: + + [Oxl2011]_, p. 646. """ A = Matrix( GF(3), @@ -717,7 +753,9 @@ def F8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 647. + REFERENCES: + + [Oxl2011]_, p. 647. """ CC = { 3: [ @@ -765,7 +803,9 @@ def Q8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 647. + REFERENCES: + + [Oxl2011]_, p. 647. """ CC = { 3: [ @@ -815,7 +855,9 @@ def L8(): sage: M.contract(e).is_isomorphic(K4ext) True - REFERENCES: [Oxl2011]_, p. 648. + REFERENCES: + + [Oxl2011]_, p. 648. """ CC = {3: ['abfg', 'bcdg', 'defg', 'cdeh', 'aefh', 'abch', 'aceg', 'bdfh'], 4: ['abcdefgh']} @@ -857,7 +899,9 @@ def S8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 648. + REFERENCES: + + [Oxl2011]_, p. 648. """ A = Matrix( GF(2), @@ -901,7 +945,9 @@ def Vamos(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 649. + REFERENCES: + + [Oxl2011]_, p. 649. """ CC = {3: ['abcd', 'abef', 'cdef', 'abgh', 'efgh'], 4: ['abcdefgh']} M = CircuitClosuresMatroid(groundset='abcdefgh', circuit_closures=CC) @@ -932,7 +978,9 @@ def T8(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 649. + REFERENCES: + + [Oxl2011]_, p. 649. """ A = Matrix( GF(3), @@ -971,7 +1019,9 @@ def J(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 650. + REFERENCES: + + [Oxl2011]_, p. 650. """ A = Matrix( GF(3), @@ -1007,7 +1057,9 @@ def P8(): sage: M.bicycle_dimension() 2 - REFERENCES: [Oxl2011]_, p. 650-1. + REFERENCES: + + [Oxl2011]_, p. 650-1. """ A = Matrix( GF(3), @@ -1048,7 +1100,9 @@ def P8pp(): sage: M.is_valid() # long time True - REFERENCES: [Oxl2011]_, p. 651. + REFERENCES: + + [Oxl2011]_, p. 651. """ CC = {3: ['abfh', 'bceg', 'cdfh', 'adeg', 'acef', 'bdfg', 'acgh', 'bdeh'], 4: ['abcdefgh']} @@ -1075,7 +1129,9 @@ def Wheel4(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 651-2. + REFERENCES: + + [Oxl2011]_, p. 651-2. """ M = Wheel(4) return M @@ -1099,7 +1155,9 @@ def Whirl4(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 652. + REFERENCES: + + [Oxl2011]_, p. 652. """ M = Whirl(4) return M @@ -1125,7 +1183,9 @@ def K33dual(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 652-3. + REFERENCES: + + [Oxl2011]_, p. 652-3. """ from sage.graphs.graph_generators import graphs @@ -1153,7 +1213,9 @@ def K33(): sage: G1.is_isomorphic(G2) True - REFERENCES: [Oxl2011]_, p. 652-3. + REFERENCES: + + [Oxl2011]_, p. 652-3. """ from sage.graphs.graph_generators import graphs @@ -1185,7 +1247,9 @@ def AG23(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 653. + REFERENCES: + + [Oxl2011]_, p. 653. """ M = AG(2, 3) return M @@ -1210,7 +1274,9 @@ def TernaryDowling3(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 654. + REFERENCES: + + [Oxl2011]_, p. 654. """ A = Matrix( GF(3), @@ -1246,7 +1312,9 @@ def R9(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 654. + REFERENCES: + + [Oxl2011]_, p. 654. """ NSC = ['abc', 'abd', 'acd', 'aef', 'agh', 'bcd', 'bfh', 'bgi', 'ceg', 'cfi', 'deh', 'dei', 'dfg', 'dhi', 'ehi'] @@ -1284,7 +1352,9 @@ def Pappus(groundset=None): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 655. + REFERENCES: + + [Oxl2011]_, p. 655. """ CC = {2: ['abc', 'def', 'ceg', 'bfg', 'cdh', 'afh', 'bdi', 'aei', 'ghi'], 3: ['abcdefghi']} @@ -1320,7 +1390,9 @@ def NonPappus(): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 655. + REFERENCES: + + [Oxl2011]_, p. 655. """ CC = {2: ['abc', 'ceg', 'bfg', 'cdh', 'afh', 'bdi', 'aei', 'ghi'], 3: ['abcdefghi']} @@ -1345,7 +1417,9 @@ def K5(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 656. + REFERENCES: + + [Oxl2011]_, p. 656. """ M = CompleteGraphic(5) return M @@ -1368,7 +1442,9 @@ def K5dual(): sage: G1.is_isomorphic(G2) True - REFERENCES: [Oxl2011]_, p. 656. + REFERENCES: + + [Oxl2011]_, p. 656. """ M = CompleteGraphic(5).dual() M.rename("M*(K5): " + repr(M)) @@ -1416,7 +1492,9 @@ def R10(): sage: matroids.catalog.R10().linear_extensions(simple=True) [] - REFERENCES: [Oxl2011]_, p. 656-7. + REFERENCES: + + [Oxl2011]_, p. 656-7. """ A = Matrix( ZZ, @@ -1450,7 +1528,9 @@ def NonDesargues(groundset=None): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 657. + REFERENCES: + + [Oxl2011]_, p. 657. """ NSC = ['acj', 'aef', 'bce', 'bfj', 'bgi', 'chi', 'dfg', 'dij', 'egh'] M = Matroid(rank=3, nonspanning_circuits=NSC) @@ -1478,7 +1558,9 @@ def R12(groundset='abcdefghijkl'): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 657. + REFERENCES: + + [Oxl2011]_, p. 657. """ A = Matrix( ZZ, @@ -1547,7 +1629,9 @@ def ExtendedTernaryGolayCode(): :class:`GolayCode ` - REFERENCES: [Oxl2011]_, p. 658. + REFERENCES: + + [Oxl2011]_, p. 658. """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 0], @@ -1583,7 +1667,9 @@ def T12(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 658-9. + REFERENCES: + + [Oxl2011]_, p. 658-9. """ A = Matrix( GF(2), @@ -1617,7 +1703,9 @@ def PG23(): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 659. + REFERENCES: + + [Oxl2011]_, p. 659. """ M = PG(2, 3) return M @@ -1665,7 +1753,9 @@ def Wheel(r, field=None, ring=None): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 659-60. + REFERENCES: + + [Oxl2011]_, p. 659-60. """ base_ring = ZZ if field is not None and ring is not None: @@ -1746,7 +1836,9 @@ def Whirl(r): ``[ 1 -1 0]`` ``[ 0 1 -1]`` - REFERENCES: [Oxl2011]_, p. 659-60. + REFERENCES: + + [Oxl2011]_, p. 659-60. """ A = Matrix(GF(3), r, 2 * r, sparse=True) for i in range(r): @@ -1798,7 +1890,9 @@ def Uniform(r, n): sage: len(M.circuit_closures()) 0 - REFERENCES: [Oxl2011]_, p. 660. + REFERENCES: + + [Oxl2011]_, p. 660. """ E = range(n) if r < n: @@ -1838,7 +1932,9 @@ def PG(n, q, x=None): PG(4, 7): Linear matroid of rank 5 on 2801 elements represented over the Finite Field of size 7 - REFERENCES: [Oxl2011]_, p. 660. + REFERENCES: + + [Oxl2011]_, p. 660. """ if x is None: x = 'x' @@ -1880,7 +1976,9 @@ def AG(n, q, x=None): sage: M = matroids.AG(4, 2); M AG(4, 2): Binary matroid of rank 5 on 16 elements, type (5, 0) - REFERENCES: [Oxl2011]_, p. 661. + REFERENCES: + + [Oxl2011]_, p. 661. """ if x is None: x = 'x' @@ -1948,7 +2046,9 @@ def Z(r, t=True): sage: Z.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 661-2. + REFERENCES: + + [Oxl2011]_, p. 661-2. """ from sage.matrix.special import identity_matrix, ones_matrix Id = Matrix(GF(2), identity_matrix(r)) @@ -2042,7 +2142,9 @@ def Spike(r, t=True, C3=[]): sage: M.equals(M.dual()) True - REFERENCES: [Oxl2011]_, p. 662. + REFERENCES: + + [Oxl2011]_, p. 662. """ if not (r >= 3): raise ValueError("The r-spike is defined for r >= 3.") @@ -2140,7 +2242,9 @@ def Theta(n): sage: M.automorphism_group().is_transitive() False - REFERENCES: [Oxl2011]_, p. 663-4. + REFERENCES: + + [Oxl2011]_, p. 663-4. """ X = ['x'+str(i) for i in range(n)] Y = ['y'+str(i) for i in range(n)] @@ -2207,7 +2311,9 @@ def Psi(r): sage: M.automorphism_group().is_transitive() True - REFERENCES: [Oxl2011]_, p. 664. + REFERENCES: + + [Oxl2011]_, p. 664. """ A = ['a'+str(i) for i in range(0, r)] B = ['b'+str(i) for i in range(0, r)] @@ -4530,7 +4636,9 @@ def NonVamos(): sage: M.is_valid() # long time True - REFERENCES: [Oxl2011]_, p. 72, 84. + REFERENCES: + + [Oxl2011]_, p. 72, 84. """ E = 'abcdefgh' CC = { @@ -4556,7 +4664,9 @@ def NotP8(): sage: M.is_valid() True - REFERENCES: [Oxl1992]_, p.512 (the first edition). + REFERENCES: + + [Oxl1992]_, p.512 (the first edition). """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 1, 1, -1], @@ -4587,7 +4697,9 @@ class of near-regular matroids. sage: M.is_valid() True - REFERENCES: [Oxl2011]_, p. 653. + REFERENCES: + + [Oxl2011]_, p. 653. """ E = 'abcdefgh' CC = {2: ['abc', 'ceh', 'fgh', 'adf', 'aeg', 'cdg', 'bdh', 'bef'], @@ -4608,8 +4720,10 @@ def P9(): sage: M.is_valid() True - REFERENCES: This is the matroid referred to as `P_9` by Oxley in his paper - "The binary matroids with no 4-wheel minor", [Oxl1987]_. + REFERENCES: + + This is the matroid referred to as `P_9` by Oxley in his paper "The binary + matroids with no 4-wheel minor", [Oxl1987]_. """ A = Matrix(GF(2), [ [1, 0, 0, 0, 1, 0, 0, 1, 1], @@ -4709,7 +4823,9 @@ def TicTacToe(): sage: M.is_valid() # long time True - REFERENCES: [Hoc]_ + REFERENCES: + + [Hoc]_ """ E = 'abcdefghi' CC = { @@ -4737,7 +4853,9 @@ def N1(): sage: M.is_valid() True - REFERENCES: [Oxl2011]_, p. 554. + REFERENCES: + + [Oxl2011]_, p. 554. """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 2, 0, 0, 1, 1], @@ -4863,7 +4981,9 @@ def N2(): sage: M.is_valid() True - REFERENCES: [Oxl2011]_, p. 554. + REFERENCES: + + [Oxl2011]_, p. 554. """ A = Matrix(GF(3), [ [1, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1], @@ -4894,7 +5014,9 @@ def D16(groundset='abcdefghijklmnop'): # A.K.A. the Carolyn Chun Matroid sage: M.is_valid() True - REFERENCES: [CMO2012]_ + REFERENCES: + + [CMO2012]_ """ A = Matrix(GF(2), [ [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0], @@ -4925,7 +5047,9 @@ def Terrahawk(): # A.K.A. the Dillon Mayhew Matroid sage: M.is_valid() True - REFERENCES: [CMO2011]_ + REFERENCES: + + [CMO2011]_ """ A = Matrix(GF(2), [ [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index fc1f5dd9cc2..2338c144190 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -3124,7 +3124,9 @@ cdef class Matroid(SageObject): A 6-dimensional polyhedron in ZZ^7 defined as the convex hull of 29 vertices - REFERENCES: [DLHK2007]_ + REFERENCES: + + [DLHK2007]_ """ from sage.geometry.polyhedron.constructor import Polyhedron from sage.modules.free_module import FreeModule @@ -3165,7 +3167,9 @@ cdef class Matroid(SageObject): A 7-dimensional polyhedron in ZZ^7 defined as the convex hull of 58 vertices - REFERENCES: [DLHK2007]_ + REFERENCES: + + [DLHK2007]_ """ from sage.geometry.polyhedron.constructor import Polyhedron from sage.modules.free_module import FreeModule @@ -5992,7 +5996,9 @@ cdef class Matroid(SageObject): sage: matroids.catalog.Vamos().girth() 4 - REFERENCES: [Oxl2011]_, p. 327. + REFERENCES: + + [Oxl2011]_, p. 327. """ for k in range(self.rank() + 2): for X in combinations(self.groundset(), k): @@ -7986,7 +7992,9 @@ cdef class Matroid(SageObject): sage: G.structure_description() 'M12' - REFERENCES: [Oxl2011]_, p. 189. + REFERENCES: + + [Oxl2011]_, p. 189. """ from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(self.bases()).automorphism_group() From 416734c36eb5ed2b4f91adaf049e0ffae298bbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Feb 2024 09:54:38 +0100 Subject: [PATCH 161/191] general cleanup of words/paths.py --- src/sage/combinat/words/paths.py | 431 ++++++++++++++++--------------- 1 file changed, 217 insertions(+), 214 deletions(-) diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index 5106615ae9b..f5506152d44 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -18,7 +18,7 @@ - Arnaud Bergeron (2008) : Initial version, path on the square grid -- Sebastien Labbe (2009-01-14) : New classes and hierarchy, doc and functions. +- Sébastien Labbé (2009-01-14) : New classes and hierarchy, doc and functions. EXAMPLES: @@ -185,22 +185,22 @@ from sage.combinat.words.word import FiniteWord_class from sage.combinat.words.alphabet import build_alphabet from sage.misc.lazy_import import lazy_import -lazy_import("sage.plot.all", ["arrow", "line", "polygon", "point", "Graphics"]) from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from .word_datatypes import (WordDatatype_str, - WordDatatype_list, - WordDatatype_tuple) - #WordDatatype_cpp_basic_string) + WordDatatype_list, + WordDatatype_tuple) +# WordDatatype_cpp_basic_string) from .word_infinite_datatypes import ( - WordDatatype_iter_with_caching, - WordDatatype_iter, - WordDatatype_callable_with_caching, - WordDatatype_callable) + WordDatatype_iter_with_caching, + WordDatatype_iter, + WordDatatype_callable_with_caching, + WordDatatype_callable) from sage.matrix.constructor import vector_on_axis_rotation_matrix +lazy_import("sage.plot.all", ["arrow", "line", "polygon", "point", "Graphics"]) lazy_import('sage.rings.number_field.number_field', 'QuadraticField') @@ -212,13 +212,13 @@ def WordPaths(alphabet, steps=None): r""" - Returns the combinatorial class of paths of the given type of steps. + Return the combinatorial class of paths of the given type of steps. INPUT: - ``alphabet`` - ordered alphabet - - ``steps`` - (default is None). It can be one of the following: + - ``steps`` - (default is ``None``). It can be one of the following: - an iterable ordered container of as many vectors as there are letters in the alphabet. The vectors are associated to the letters @@ -248,7 +248,7 @@ def WordPaths(alphabet, steps=None): OUTPUT: - - The combinatorial class of all paths of the given type. + The combinatorial class of all paths of the given type. EXAMPLES: @@ -321,10 +321,10 @@ def WordPaths(alphabet, steps=None): ... TypeError: Unknown type of steps : square_gridd """ - #Construction of the alphabet + # Construction of the alphabet alphabet = build_alphabet(alphabet) - #If no steps are given, they are guessed from the alphabet + # If no steps are given, they are guessed from the alphabet if steps is None: if alphabet.cardinality() == 2: steps = 'north_east' @@ -335,7 +335,7 @@ def WordPaths(alphabet, steps=None): else: raise TypeError("Unable to make a class WordPaths from %s" % alphabet) - #Returns the class of WordPaths according to the given type of paths + # Return the class of WordPaths according to the given type of paths if isinstance(steps, str): if steps in ('square_grid', 'square'): return WordPaths_square_grid(alphabet=alphabet) @@ -405,25 +405,25 @@ def __init__(self, alphabet, steps): sage: WordPaths_all('ab', d) Traceback (most recent call last): ... - ValueError: Can't make vectors from steps + ValueError: cannot make vectors from steps sage: d = ((1,1), (-1,1,0)) sage: WordPaths_all('ab', d) Traceback (most recent call last): ... - ValueError: Can't make summable vectors from steps + ValueError: cannot make summable vectors from steps """ - #Construction of the words class + # Construction of the words class FiniteWords.__init__(self, alphabet) alphabet = self.alphabet() - #Checking the size of alphabet and steps + # Checking the size of alphabet and steps ls = len(steps) la = alphabet.cardinality() - if la != ls and la != 2*ls: + if la != ls and la != 2 * ls: raise TypeError("size of steps (=%s) must equal the size of alphabet (=%s) or half the size of alphabet" % (len(steps), alphabet.cardinality())) - #Construction of the steps + # Construction of the steps from sage.structure.element import Vector if all(isinstance(x, Vector) for x in steps): vsteps = steps @@ -431,13 +431,13 @@ def __init__(self, alphabet, steps): try: vsteps = [vector(s) for s in steps] except (TypeError): - raise ValueError("Can't make vectors from steps") + raise ValueError("cannot make vectors from steps") try: s = sum(vsteps) except (TypeError, AttributeError): - raise ValueError("Can't make summable vectors from steps") + raise ValueError("cannot make summable vectors from steps") - #Complete vsteps with the opposite vectors if needed + # Complete vsteps with the opposite vectors if needed if la == 2 * ls: vsteps += [-v for v in vsteps] @@ -457,9 +457,9 @@ def __eq__(self, other): False """ return self is other or (type(self) is type(other) and - self.alphabet() == other.alphabet() and - self.vector_space() == other.vector_space() and - self.letters_to_steps() == other.letters_to_steps()) + self.alphabet() == other.alphabet() and + self.vector_space() == other.vector_space() and + self.letters_to_steps() == other.letters_to_steps()) def __ne__(self, other): r""" @@ -478,7 +478,7 @@ def __ne__(self, other): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -524,38 +524,38 @@ def _element_classes(self): dimension = self._vector_space.dimension() if dimension == 2: return { - 'list': FiniteWordPath_2d_list, - 'str': FiniteWordPath_2d_str, - 'tuple': FiniteWordPath_2d_tuple, - 'callable_with_caching': FiniteWordPath_2d_callable_with_caching, - 'callable': FiniteWordPath_2d_callable, - 'iter_with_caching': FiniteWordPath_2d_iter_with_caching, - 'iter': FiniteWordPath_2d_iter, + 'list': FiniteWordPath_2d_list, + 'str': FiniteWordPath_2d_str, + 'tuple': FiniteWordPath_2d_tuple, + 'callable_with_caching': FiniteWordPath_2d_callable_with_caching, + 'callable': FiniteWordPath_2d_callable, + 'iter_with_caching': FiniteWordPath_2d_iter_with_caching, + 'iter': FiniteWordPath_2d_iter, } elif dimension == 3: return { - 'list': FiniteWordPath_3d_list, - 'str': FiniteWordPath_3d_str, - 'tuple': FiniteWordPath_3d_tuple, - 'callable_with_caching': FiniteWordPath_3d_callable_with_caching, - 'callable': FiniteWordPath_3d_callable, - 'iter_with_caching': FiniteWordPath_3d_iter_with_caching, - 'iter': FiniteWordPath_3d_iter, + 'list': FiniteWordPath_3d_list, + 'str': FiniteWordPath_3d_str, + 'tuple': FiniteWordPath_3d_tuple, + 'callable_with_caching': FiniteWordPath_3d_callable_with_caching, + 'callable': FiniteWordPath_3d_callable, + 'iter_with_caching': FiniteWordPath_3d_iter_with_caching, + 'iter': FiniteWordPath_3d_iter, } else: return { - 'list': FiniteWordPath_all_list, - 'str': FiniteWordPath_all_str, - 'tuple': FiniteWordPath_all_tuple, - 'callable_with_caching': FiniteWordPath_all_callable_with_caching, - 'callable': FiniteWordPath_all_callable, - 'iter_with_caching': FiniteWordPath_all_iter_with_caching, - 'iter': FiniteWordPath_all_iter, + 'list': FiniteWordPath_all_list, + 'str': FiniteWordPath_all_str, + 'tuple': FiniteWordPath_all_tuple, + 'callable_with_caching': FiniteWordPath_all_callable_with_caching, + 'callable': FiniteWordPath_all_callable, + 'iter_with_caching': FiniteWordPath_all_iter_with_caching, + 'iter': FiniteWordPath_all_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" - Returns a string representation of self. + Return a string representation of ``self``. EXAMPLES:: @@ -566,9 +566,9 @@ def __repr__(self): """ return "Word Paths over %s steps" % self.alphabet().cardinality() - def letters_to_steps(self): + def letters_to_steps(self) -> dict: r""" - Returns the dictionary mapping letters to vectors (steps). + Return the dictionary mapping letters to vectors (steps). EXAMPLES:: @@ -632,18 +632,17 @@ def __init__(self, alphabet): Word Paths on the square grid sage: P == loads(dumps(P)) True - """ - #Construction of the steps - d = [(1 ,0), (0,1), (-1,0), (0,-1)] + # Construction of the steps + d = [(1, 0), (0, 1), (-1, 0), (0, -1)] - #Construction of the class + # Construction of the class super().__init__(alphabet, steps=d) @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -661,16 +660,16 @@ def _element_classes(self): """ return { - 'list': FiniteWordPath_square_grid_list, - 'str': FiniteWordPath_square_grid_str, - 'tuple': FiniteWordPath_square_grid_tuple, - 'callable_with_caching': FiniteWordPath_square_grid_callable_with_caching, - 'callable': FiniteWordPath_square_grid_callable, - 'iter_with_caching': FiniteWordPath_square_grid_iter_with_caching, - 'iter': FiniteWordPath_square_grid_iter, + 'list': FiniteWordPath_square_grid_list, + 'str': FiniteWordPath_square_grid_str, + 'tuple': FiniteWordPath_square_grid_tuple, + 'callable_with_caching': FiniteWordPath_square_grid_callable_with_caching, + 'callable': FiniteWordPath_square_grid_callable, + 'iter_with_caching': FiniteWordPath_square_grid_iter_with_caching, + 'iter': FiniteWordPath_square_grid_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -701,20 +700,19 @@ def __init__(self, alphabet): Word Paths on the triangle grid sage: P == loads(dumps(P)) True - """ K = QuadraticField(3, 'sqrt3') sqrt3 = K.gen() - #Construction of the steps - d = (vector(K, (1 ,0 )), - vector(K, (ZZ(1)/ZZ(2), sqrt3/2)), - vector(K, (ZZ(-1)/ZZ(2), sqrt3/2)), - vector(K, (-1 , 0 )), - vector(K, (ZZ(-1)/ZZ(2), -sqrt3/2 )), - vector(K, (ZZ(1)/ZZ(2), -sqrt3/2 ))) + # Construction of the steps + d = (vector(K, (1, 0)), + vector(K, (ZZ(1) / ZZ(2), sqrt3 / 2)), + vector(K, (ZZ(-1) / ZZ(2), sqrt3 / 2)), + vector(K, (-1, 0)), + vector(K, (ZZ(-1) / ZZ(2), -sqrt3 / 2)), + vector(K, (ZZ(1) / ZZ(2), -sqrt3 / 2))) - #Construction of the class + # Construction of the class super().__init__(alphabet, steps=d) self._infinite_word_class = None @@ -723,7 +721,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -741,16 +739,16 @@ def _element_classes(self): """ return { - 'list': FiniteWordPath_triangle_grid_list, - 'str': FiniteWordPath_triangle_grid_str, - 'tuple': FiniteWordPath_triangle_grid_tuple, - 'callable_with_caching': FiniteWordPath_triangle_grid_callable_with_caching, - 'callable': FiniteWordPath_triangle_grid_callable, - 'iter_with_caching': FiniteWordPath_triangle_grid_iter_with_caching, - 'iter': FiniteWordPath_triangle_grid_iter, + 'list': FiniteWordPath_triangle_grid_list, + 'str': FiniteWordPath_triangle_grid_str, + 'tuple': FiniteWordPath_triangle_grid_tuple, + 'callable_with_caching': FiniteWordPath_triangle_grid_callable_with_caching, + 'callable': FiniteWordPath_triangle_grid_callable, + 'iter_with_caching': FiniteWordPath_triangle_grid_iter_with_caching, + 'iter': FiniteWordPath_triangle_grid_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -781,9 +779,8 @@ def __init__(self, alphabet): Word Paths on the hexagonal grid sage: P == loads(dumps(P)) True - """ - #Construction of the class + # Construction of the class super().__init__(alphabet) self._infinite_word_class = None @@ -792,7 +789,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -810,16 +807,16 @@ def _element_classes(self): """ return { - 'list': FiniteWordPath_hexagonal_grid_list, - 'str': FiniteWordPath_hexagonal_grid_str, - 'tuple': FiniteWordPath_hexagonal_grid_tuple, - 'callable_with_caching': FiniteWordPath_hexagonal_grid_callable_with_caching, - 'callable': FiniteWordPath_hexagonal_grid_callable, - 'iter_with_caching': FiniteWordPath_hexagonal_grid_iter_with_caching, - 'iter': FiniteWordPath_hexagonal_grid_iter, + 'list': FiniteWordPath_hexagonal_grid_list, + 'str': FiniteWordPath_hexagonal_grid_str, + 'tuple': FiniteWordPath_hexagonal_grid_tuple, + 'callable_with_caching': FiniteWordPath_hexagonal_grid_callable_with_caching, + 'callable': FiniteWordPath_hexagonal_grid_callable, + 'iter_with_caching': FiniteWordPath_hexagonal_grid_iter_with_caching, + 'iter': FiniteWordPath_hexagonal_grid_iter, } - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -852,8 +849,9 @@ def __init__(self, alphabet): sage: P == loads(dumps(P)) True """ - #Construction of the class - d = [(1,0,0), (0,1,0), (0,0,1), (-1,0,0), (0,-1,0), (0,0,-1)] + # Construction of the class + d = [(1, 0, 0), (0, 1, 0), (0, 0, 1), + (-1, 0, 0), (0, -1, 0), (0, 0, -1)] super().__init__(alphabet, steps=d) self._infinite_word_class = None self._finite_word_class = FiniteWordPath_cube_grid @@ -861,7 +859,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -879,15 +877,15 @@ def _element_classes(self): """ return {'list': FiniteWordPath_cube_grid_list, - 'str': FiniteWordPath_cube_grid_str, - 'tuple': FiniteWordPath_cube_grid_tuple, - 'callable_with_caching': FiniteWordPath_cube_grid_callable_with_caching, - 'callable': FiniteWordPath_cube_grid_callable, - 'iter_with_caching': FiniteWordPath_cube_grid_iter_with_caching, - 'iter': FiniteWordPath_cube_grid_iter, - } - - def __repr__(self): + 'str': FiniteWordPath_cube_grid_str, + 'tuple': FiniteWordPath_cube_grid_tuple, + 'callable_with_caching': FiniteWordPath_cube_grid_callable_with_caching, + 'callable': FiniteWordPath_cube_grid_callable, + 'iter_with_caching': FiniteWordPath_cube_grid_iter_with_caching, + 'iter': FiniteWordPath_cube_grid_iter, + } + + def __repr__(self) -> str: r""" EXAMPLES:: @@ -919,8 +917,8 @@ def __init__(self, alphabet): sage: P == loads(dumps(P)) True """ - #Construction of the class - d = [(1,1), (1,-1)] + # Construction of the class + d = [(1, 1), (1, -1)] super().__init__(alphabet, steps=d) self._infinite_word_class = None @@ -929,7 +927,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -947,15 +945,15 @@ def _element_classes(self): """ return {'list': FiniteWordPath_dyck_list, - 'str': FiniteWordPath_dyck_str, - 'tuple': FiniteWordPath_dyck_tuple, - 'callable_with_caching': FiniteWordPath_dyck_callable_with_caching, - 'callable': FiniteWordPath_dyck_callable, - 'iter_with_caching': FiniteWordPath_dyck_iter_with_caching, - 'iter': FiniteWordPath_dyck_iter, - } - - def __repr__(self): + 'str': FiniteWordPath_dyck_str, + 'tuple': FiniteWordPath_dyck_tuple, + 'callable_with_caching': FiniteWordPath_dyck_callable_with_caching, + 'callable': FiniteWordPath_dyck_callable, + 'iter_with_caching': FiniteWordPath_dyck_iter_with_caching, + 'iter': FiniteWordPath_dyck_iter, + } + + def __repr__(self) -> str: r""" EXAMPLES:: @@ -988,8 +986,8 @@ def __init__(self, alphabet): sage: P == loads(dumps(P)) True """ - #Construction of the class - d = [(0,1), (1,0)] + # Construction of the class + d = [(0, 1), (1, 0)] super().__init__(alphabet, steps=d) self._infinite_word_class = None self._finite_word_class = FiniteWordPath_north_east @@ -997,7 +995,7 @@ def __init__(self, alphabet): @lazy_attribute def _element_classes(self): r""" - Returns a dictionary that gives the class of the elements of self. + Return a dictionary that gives the class of the elements of ``self``. The word may be finite (infinite or of unknown length is not supported yet). @@ -1015,15 +1013,15 @@ def _element_classes(self): """ return {'list': FiniteWordPath_north_east_list, - 'str': FiniteWordPath_north_east_str, - 'tuple': FiniteWordPath_north_east_tuple, - 'callable_with_caching': FiniteWordPath_north_east_callable_with_caching, - 'callable': FiniteWordPath_north_east_callable, - 'iter_with_caching': FiniteWordPath_north_east_iter_with_caching, - 'iter': FiniteWordPath_north_east_iter, - } - - def __repr__(self): + 'str': FiniteWordPath_north_east_str, + 'tuple': FiniteWordPath_north_east_tuple, + 'callable_with_caching': FiniteWordPath_north_east_callable_with_caching, + 'callable': FiniteWordPath_north_east_callable, + 'iter_with_caching': FiniteWordPath_north_east_iter_with_caching, + 'iter': FiniteWordPath_north_east_iter, + } + + def __repr__(self) -> str: r""" EXAMPLES:: @@ -1042,9 +1040,9 @@ def __repr__(self): ####################################################################### class FiniteWordPath_all(SageObject): - def _repr_(self): + def _repr_(self) -> str: r""" - Returns a string representation of this path. + Return a string representation of this path. EXAMPLES:: @@ -1058,7 +1056,7 @@ def _repr_(self): def points(self, include_last=True): r""" - Returns an iterator yielding a list of points used to draw the path + Return an iterator yielding a list of points used to draw the path represented by this word. INPUT: @@ -1093,11 +1091,11 @@ def points(self, include_last=True): def start_point(self): r""" - Return the starting point of self. + Return the starting point of ``self``. OUTPUT: - vector + vector EXAMPLES:: @@ -1114,7 +1112,7 @@ def start_point(self): @cached_method def end_point(self): r""" - Returns the end point of the path. + Return the end point of the path. EXAMPLES:: @@ -1138,10 +1136,10 @@ def end_point(self): def directive_vector(self): r""" - Returns the directive vector of self. + Return the directive vector of ``self``. The directive vector is the vector starting at the start point - and ending at the end point of the path self. + and ending at the end point of the path ``self``. EXAMPLES:: @@ -1160,9 +1158,11 @@ def directive_vector(self): """ return self.end_point() - self.start_point() - def is_closed(self): + def is_closed(self) -> bool: r""" - Returns True if the path is closed, i.e. if the origin and the end of + Return ``True`` if the path is closed. + + A path is closed if the origin and the end of the path are equal. EXAMPLES:: @@ -1179,10 +1179,12 @@ def is_closed(self): """ return self.start_point() == self.end_point() - def is_simple(self): + def is_simple(self) -> bool: r""" - Returns True if the path is simple, i.e. if all its points are - distincts. + Return ``True`` if the path is simple. + + A path is simple if all its points are + distinct. If the path is closed, the last point is not considered. @@ -1215,9 +1217,9 @@ def is_simple(self): return False return True - def tikz_trajectory(self): + def tikz_trajectory(self) -> str: r""" - Returns the trajectory of self as a tikz str. + Return the trajectory of ``self`` as a ``tikz`` string. EXAMPLES:: @@ -1228,8 +1230,7 @@ def tikz_trajectory(self): """ from sage.misc.functional import N as n - f = lambda x: n(x,digits=3) - l = [str(tuple(map(f, pt))) for pt in self.points()] + l = (str(tuple(n(x, digits=3) for x in pt)) for pt in self.points()) return ' -- '.join(l) def projected_point_iterator(self, v=None, ring=None): @@ -1292,7 +1293,7 @@ def projected_point_iterator(self, v=None, ring=None): yield R * q def plot_projection(self, v=None, letters=None, color=None, ring=None, - size=12, kind='right'): + size=12, kind='right'): r""" Return an image of the projection of the successive points of the path into the space orthogonal to the given vector. @@ -1405,7 +1406,7 @@ def plot_projection(self, v=None, letters=None, color=None, ring=None, if color is None: from sage.plot.all import hue A = self.parent().alphabet() - color = {a: hue(A.rank(a)/float(A.cardinality())) for a in A} + color = {a: hue(A.rank(a) / float(A.cardinality())) for a in A} it = self.projected_point_iterator(v, ring=ring) if kind == 'right': next(it) @@ -1464,7 +1465,7 @@ def projected_path(self, v=None, ring=None): R = vector_on_axis_rotation_matrix(v, 0, ring=ring)[1:] d = self.parent().letters_to_steps() A = self.parent().alphabet() - nvvectors = [R*d[a] for a in A] + nvvectors = [R * d[a] for a in A] projected_parent = WordPaths(A, nvvectors) return projected_parent(self) @@ -1488,13 +1489,13 @@ def is_tangent(self): class FiniteWordPath_2d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), - fill=True, filloptions=dict(rgbcolor='red',alpha=0.2), - startpoint=True, startoptions=dict(rgbcolor='red',pointsize=100), - endarrow=True, arrowoptions=dict(rgbcolor='red',arrowsize=20,width=3), - gridlines=False, gridoptions=dict()): + def plot(self, pathoptions=dict(rgbcolor='red', thickness=3), + fill=True, filloptions=dict(rgbcolor='red', alpha=0.2), + startpoint=True, startoptions=dict(rgbcolor='red', pointsize=100), + endarrow=True, arrowoptions=dict(rgbcolor='red', arrowsize=20, width=3), + gridlines=False, gridoptions=dict()): r""" - Returns a 2d Graphics illustrating the path. + Return a 2d Graphics illustrating the path. INPUT: @@ -1579,15 +1580,15 @@ def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), #################### pts = [[RR(i) for i in x] for x in pts] - #Inside + # Inside if fill and self.is_closed(): G += polygon(pts, **filloptions) - #Startpoint + # Startpoint if startpoint: G += point(pts[0], **startoptions) - #The path itself + # The path itself if endarrow and not self.is_empty(): G += line(pts[:-1], **pathoptions) G += arrow(pts[-2], pts[-1], **arrowoptions) @@ -1597,11 +1598,11 @@ def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), G.axes(False) G.set_aspect_ratio(1) - #gridlines - ###############BUG############## - #Gridlines doesn't work fine. - #It should be gridlines="integers" - ###############BUG############## + # gridlines + # ############## BUG ############## + # Gridlines doesn't work fine. + # It should be gridlines="integers" + # ############## BUG ############## if gridlines: G = G.show(gridlines=True, **gridoptions) @@ -1609,7 +1610,7 @@ def plot(self, pathoptions=dict(rgbcolor='red',thickness=3), def animate(self): r""" - Returns an animation object illustrating the path growing step by step. + Return an animation object illustrating the path growing step by step. EXAMPLES:: @@ -1675,17 +1676,17 @@ def animate(self): #################### #################### - #Bug: plot needs float for coordinates + # Bug: plot needs float for coordinates #################### #################### pts = [[RR(i) for i in x] for x in pts] - images = [line(pts[:i]) for i in range(1,len(pts)+1)] + images = [line(pts[:i]) for i in range(1, len(pts) + 1)] if self.is_closed(): images.append(polygon(pts)) - #Get the window of the last image + # Get the window of the last image last_image = images[-1] kwds = {} kwds['xmin'] = last_image.xmin() @@ -1699,7 +1700,7 @@ def animate(self): def plot_directive_vector(self, options=dict(rgbcolor='blue')): r""" - Returns an arrow 2d graphics that goes from the start of the path + Return an arrow 2d graphics that goes from the start of the path to the end. INPUT: @@ -1740,7 +1741,7 @@ def plot_directive_vector(self, options=dict(rgbcolor='blue')): def area(self): r""" - Returns the area of a closed path. + Return the area of a closed path. INPUT: @@ -1760,7 +1761,7 @@ def area(self): def height(self): r""" - Returns the height of self. + Return the height of ``self``. The height of a `2d`-path is merely the difference between the highest and the lowest `y`-coordinate of each @@ -1827,7 +1828,7 @@ def height_vector(self): def width(self): r""" - Returns the width of self. + Return the width of ``self``. The height of a `2d`-path is merely the difference between the rightmost and the leftmost `x`-coordinate of each @@ -1835,7 +1836,7 @@ def width(self): OUTPUT: - non negative real number + non negative real number EXAMPLES:: @@ -1894,7 +1895,7 @@ def width_vector(self): def xmin(self): r""" - Returns the minimum of the x-coordinates of the path. + Return the minimum of the x-coordinates of the path. EXAMPLES:: @@ -1917,11 +1918,11 @@ def xmin(self): sage: w.xmin() 0.000000000000000 """ - return min(x for (x,_) in self.points()) + return min(x for (x, _) in self.points()) def ymin(self): r""" - Returns the minimum of the y-coordinates of the path. + Return the minimum of the y-coordinates of the path. EXAMPLES:: @@ -1944,11 +1945,11 @@ def ymin(self): sage: w.ymin() 0.000000000000000 """ - return min(y for (_,y) in self.points()) + return min(y for (_, y) in self.points()) def xmax(self): r""" - Returns the maximum of the x-coordinates of the path. + Return the maximum of the x-coordinates of the path. EXAMPLES:: @@ -1971,11 +1972,11 @@ def xmax(self): sage: w.xmax() 4.50000000000000 """ - return max(x for (x,_) in self.points()) + return max(x for (x, _) in self.points()) def ymax(self): r""" - Returns the maximum of the y-coordinates of the path. + Return the maximum of the y-coordinates of the path. EXAMPLES:: @@ -1998,12 +1999,12 @@ def ymax(self): sage: w.ymax() 2.59807621135332 """ - return max(y for (_,y) in self.points()) + return max(y for (_, y) in self.points()) class FiniteWordPath_3d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), - startpoint=True, startoptions=dict(rgbcolor='red',size=10)): + def plot(self, pathoptions=dict(rgbcolor='red', arrow_head=True, thickness=3), + startpoint=True, startoptions=dict(rgbcolor='red', size=10)): r""" INPUT: @@ -2031,9 +2032,9 @@ def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), Graphics3d Object """ - #The following line seems not to work for 3d - #G = Graphics() - #so we draw to start a small almost invisible point instead: + # The following line seems not to work for 3d + # G = Graphics() + # so we draw to start a small almost invisible point instead: G = point([self.start_point()], size=1) pts = list(self.points()) if startpoint: @@ -2050,9 +2051,9 @@ def plot(self, pathoptions=dict(rgbcolor='red',arrow_head=True,thickness=3), ####################################################################### class FiniteWordPath_square_grid(FiniteWordPath_2d): - def is_closed(self): + def is_closed(self) -> bool: r""" - Returns True if self represents a closed path and False otherwise. + Return whether ``self`` represents a closed path. EXAMPLES:: @@ -2073,7 +2074,7 @@ def is_closed(self): def area(self): r""" - Returns the area of a closed path. + Return the area of a closed path. INPUT: @@ -2177,10 +2178,12 @@ def _area_vh(self, x=0, y=0): x -= 1 return area // 2 - def is_simple(self): + def is_simple(self) -> bool: r""" - Returns True if the path is simple, i.e. if all its points are - distincts. + Return whether the path is simple. + + A path is simple if all its points are + distinct. If the path is closed, the last point is not considered. @@ -2217,9 +2220,9 @@ def is_simple(self): """ return super().is_simple() - def tikz_trajectory(self): + def tikz_trajectory(self) -> str: r""" - Returns the trajectory of self as a tikz str. + Return the trajectory of ``self`` as a ``tikz`` string. EXAMPLES:: @@ -2246,7 +2249,7 @@ class FiniteWordPath_triangle_grid(FiniteWordPath_2d): # redefined here with conversion to RR in order to avoid this problem def xmin(self): r""" - Returns the minimum of the x-coordinates of the path. + Return the minimum of the x-coordinates of the path. EXAMPLES:: @@ -2257,11 +2260,11 @@ def xmin(self): sage: w.xmin() -3.00000000000000 """ - return min(RR(x) for (x,_) in self.points()) + return min(RR(x) for (x, _) in self.points()) def ymin(self): r""" - Returns the minimum of the y-coordinates of the path. + Return the minimum of the y-coordinates of the path. EXAMPLES:: @@ -2272,11 +2275,11 @@ def ymin(self): sage: w.ymin() -0.866025403784439 """ - return min(RR(y) for (_,y) in self.points()) + return min(RR(y) for (_, y) in self.points()) def xmax(self): r""" - Returns the maximum of the x-coordinates of the path. + Return the maximum of the x-coordinates of the path. EXAMPLES:: @@ -2287,11 +2290,11 @@ def xmax(self): sage: w.xmax() 4.00000000000000 """ - return max(RR(x) for (x,_) in self.points()) + return max(RR(x) for (x, _) in self.points()) def ymax(self): r""" - Returns the maximum of the y-coordinates of the path. + Return the maximum of the y-coordinates of the path. EXAMPLES:: @@ -2302,7 +2305,7 @@ def ymax(self): sage: w.ymax() 8.66025403784439 """ - return max(RR(y) for (_,y) in self.points()) + return max(RR(y) for (_, y) in self.points()) # TODO: faire une verification du mot pour etre sur hexagonal grid @@ -2353,7 +2356,7 @@ class FiniteWordPath_dyck(FiniteWordPath_2d): # # ####################################################################### -##### Finite paths ##### +# #### Finite paths #### class FiniteWordPath_all_list(WordDatatype_list, FiniteWordPath_all, FiniteWord_class): r""" @@ -2416,7 +2419,7 @@ class FiniteWordPath_all_callable(WordDatatype_callable, FiniteWordPath_all, Fin pass -##### Finite paths on 2d ##### +# #### Finite paths on 2d #### class FiniteWordPath_2d_list(WordDatatype_list, FiniteWordPath_2d, FiniteWord_class): r""" @@ -2479,7 +2482,7 @@ class FiniteWordPath_2d_callable(WordDatatype_callable, FiniteWordPath_2d, Finit pass -##### Finite paths on 3d ##### +# #### Finite paths on 3d #### class FiniteWordPath_3d_list(WordDatatype_list, FiniteWordPath_3d, FiniteWord_class): r""" @@ -2542,7 +2545,7 @@ class FiniteWordPath_3d_callable(WordDatatype_callable, FiniteWordPath_3d, Finit pass -##### Finite paths on square grid ##### +# #### Finite paths on square grid #### class FiniteWordPath_square_grid_list(WordDatatype_list, FiniteWordPath_square_grid, FiniteWord_class): r""" @@ -2605,12 +2608,12 @@ class FiniteWordPath_square_grid_callable(WordDatatype_callable, FiniteWordPath_ pass -##### Unknown length paths on square grid (experimental) ##### +# #### Unknown length paths on square grid (experimental) #### -#class WordPath_square_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_square_grid, Word_class): +# class WordPath_square_grid_iter_with_caching(WordDatatype_iter_with_caching, FiniteWordPath_square_grid, Word_class): # pass -##### Finite paths on triangle grid ##### +# #### Finite paths on triangle grid #### class FiniteWordPath_triangle_grid_list(WordDatatype_list, FiniteWordPath_triangle_grid, FiniteWord_class): r""" @@ -2673,7 +2676,7 @@ class FiniteWordPath_triangle_grid_callable(WordDatatype_callable, FiniteWordPat pass -##### Finite paths on hexagonal grid ##### +# #### Finite paths on hexagonal grid #### class FiniteWordPath_hexagonal_grid_list(WordDatatype_list, FiniteWordPath_hexagonal_grid, FiniteWord_class): r""" @@ -2736,7 +2739,7 @@ class FiniteWordPath_hexagonal_grid_callable(WordDatatype_callable, FiniteWordPa pass -##### Finite paths on cube grid ##### +# #### Finite paths on cube grid #### class FiniteWordPath_cube_grid_list(WordDatatype_list, FiniteWordPath_cube_grid, FiniteWord_class): r""" @@ -2799,7 +2802,7 @@ class FiniteWordPath_cube_grid_callable(WordDatatype_callable, FiniteWordPath_cu pass -##### Finite paths on north_east ##### +# #### Finite paths on north_east #### class FiniteWordPath_north_east_list(WordDatatype_list, FiniteWordPath_north_east, FiniteWord_class): r""" @@ -2862,7 +2865,7 @@ class FiniteWordPath_north_east_callable(WordDatatype_callable, FiniteWordPath_n pass -##### Finite paths on dyck ##### +# #### Finite paths on dyck #### class FiniteWordPath_dyck_list(WordDatatype_list, FiniteWordPath_dyck, FiniteWord_class): r""" From d06d2830494110fefd2254b23e16dc29d0770332 Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Thu, 8 Feb 2024 12:11:33 +0200 Subject: [PATCH 162/191] Optimizations suggested by tscrim --- src/sage/matroids/circuits_matroid.pyx | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx index 4cf16b6ee32..bbcf6ce8319 100644 --- a/src/sage/matroids/circuits_matroid.pyx +++ b/src/sage/matroids/circuits_matroid.pyx @@ -714,27 +714,37 @@ cdef class CircuitsMatroid(Matroid): False """ from itertools import combinations_with_replacement + cdef int i, j, k, S_len + cdef frozenset C1, C2, C3, I12, U12 + cdef bint flag for (i, j) in combinations_with_replacement(sorted(self._k_C), 2): # loop through all circuit length pairs (i, j) with i <= j for C1 in self._k_C[i]: if not C1: # the empty set can't be a circuit return False for C2 in self._k_C[j]: - if C1 < C2: # a circuit can't be a subset of another circuit + I12 = C1 & C2 + if not I12: # C1 and C2 are disjoint; nothing to test + continue + if len(I12) == len(C1): + if len(C1) == len(C2): # they are the same circuit + break + # C1 < C2; a circuit can't be a subset of another circuit return False - if C1 == C2: - break # check circuit elimination axiom - UC12 = set(C1) | set(C2) - for e in C1 & C2: + U12 = C1 | C2 + S_len = len(U12) - 1 # the size of S below + for e in I12: flag = False - S = UC12 - {e} + S = U12 - {e} for k in self._k_C: - if k <= len(S) and not flag: + if k <= S_len: for C3 in self._k_C[k]: if C3 <= S: flag = True break + if flag: + break if not flag: return False return True From b63a2e8ab009e721f03909f24475b0afe64e7216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Feb 2024 14:27:02 +0100 Subject: [PATCH 163/191] add type annotations to gens methods --- .../examples/finite_dimensional_lie_algebras_with_basis.py | 2 +- .../categories/finite_dimensional_modules_with_basis.py | 2 +- src/sage/categories/pushout.py | 2 +- src/sage/categories/semigroups.py | 2 +- src/sage/coding/linear_code_no_metric.py | 2 +- src/sage/combinat/colored_permutations.py | 2 +- src/sage/combinat/free_dendriform_algebra.py | 2 +- src/sage/combinat/free_prelie_algebra.py | 2 +- src/sage/geometry/hyperplane_arrangement/arrangement.py | 2 +- src/sage/geometry/linear_expression.py | 2 +- src/sage/geometry/toric_lattice.py | 2 +- src/sage/groups/abelian_gps/abelian_group.py | 4 ++-- src/sage/groups/abelian_gps/dual_abelian_group.py | 2 +- src/sage/groups/additive_abelian/additive_abelian_group.py | 2 +- src/sage/groups/cactus_group.py | 4 ++-- src/sage/groups/kernel_subgroup.py | 2 +- src/sage/groups/lie_gps/nilpotent_lie_group.py | 2 +- src/sage/groups/matrix_gps/finitely_generated.py | 2 +- src/sage/groups/raag.py | 2 +- src/sage/libs/eclib/interface.py | 2 +- src/sage/modular/arithgroup/arithgroup_generic.py | 2 +- src/sage/modules/fg_pid/fgp_module.py | 2 +- src/sage/modules/free_module.py | 4 ++-- src/sage/modules/quotient_module.py | 4 ++-- src/sage/modules/submodule.py | 2 +- src/sage/modules/torsion_quadratic_module.py | 2 +- src/sage/monoids/free_abelian_monoid.py | 6 +++--- src/sage/monoids/monoid.py | 6 +++--- src/sage/quadratic_forms/bqf_class_group.py | 2 +- src/sage/quivers/algebra.py | 4 ++-- src/sage/quivers/path_semigroup.py | 4 ++-- 31 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py index 9f221f1432e..e8db64d11a9 100644 --- a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py @@ -242,7 +242,7 @@ def basis(self): lie_algebra_generators = basis - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 1462073c5f2..a4d012f89b7 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -36,7 +36,7 @@ class FiniteDimensionalModulesWithBasis(CategoryWithAxiom_over_base_ring): class ParentMethods: - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index aa2e5dc9f4f..55cd22d87dd 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3797,7 +3797,7 @@ def __call__(self, R): return PermutationGroup([g for g in (R.gens() + self.gens()) if not g.is_one()], domain=self._domain) - def gens(self): + def gens(self) -> tuple: """ EXAMPLES:: diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index 8f2c57feeb9..7c894a03112 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -906,7 +906,7 @@ def algebra_generators(self): # gens / monoid/group/*_generators, these methods could possibly # be removed in favor of aliases gens -> xxx_generators in # the Algebras.FinitelyGenerated hierarchy - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py index 2ae0879fa74..8bc9b8bf360 100644 --- a/src/sage/coding/linear_code_no_metric.py +++ b/src/sage/coding/linear_code_no_metric.py @@ -373,7 +373,7 @@ def rate(self): return self.dimension() / self.length() @cached_method - def gens(self): + def gens(self) -> list: r""" Return the generators of this code as a list of vectors. diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 2d6a6c31aaa..6a9e5598381 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -651,7 +651,7 @@ def _inverse_simple_reflections(self): return {i: ~s[i] for i in self.index_set()} @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/combinat/free_dendriform_algebra.py b/src/sage/combinat/free_dendriform_algebra.py index fb1cb498c79..2b3ce0ae216 100644 --- a/src/sage/combinat/free_dendriform_algebra.py +++ b/src/sage/combinat/free_dendriform_algebra.py @@ -285,7 +285,7 @@ def change_ring(self, R): """ return FreeDendriformAlgebra(R, names=self.variable_names()) - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` (as an algebra). diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index cb81e811ade..88d54b1f443 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -348,7 +348,7 @@ def change_ring(self, R): """ return FreePreLieAlgebra(R, names=self.variable_names()) - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` (as an algebra). diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 7f6ad9b6843..48b77950153 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3851,7 +3851,7 @@ def ngens(self): return len(self._names) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the coordinate hyperplanes. diff --git a/src/sage/geometry/linear_expression.py b/src/sage/geometry/linear_expression.py index ad2f4e83c44..547d2eeb5a3 100644 --- a/src/sage/geometry/linear_expression.py +++ b/src/sage/geometry/linear_expression.py @@ -534,7 +534,7 @@ def ngens(self): return len(self._names) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/geometry/toric_lattice.py b/src/sage/geometry/toric_lattice.py index 4a34a6a510e..d2816aa7287 100644 --- a/src/sage/geometry/toric_lattice.py +++ b/src/sage/geometry/toric_lattice.py @@ -1511,7 +1511,7 @@ def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=No raise ValueError('You may not specify both positive_point and positive_dual_point.') self._flip_sign_of_generator = (scalar_product < 0) - def gens(self): + def gens(self) -> tuple: """ Return the generators of the quotient. diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 97c94e26574..bbe793b3625 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -928,7 +928,7 @@ def gen(self, i=0): x[i] = 1 return self.element_class(self, x) - def gens(self): + def gens(self) -> tuple: """ Return the generators of the group. @@ -1875,7 +1875,7 @@ def _repr_(self): s += '{' + ', '.join(map(str, self.gens())) + '}' return s - def gens(self): + def gens(self) -> tuple: """ Return the generators for this subgroup. diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index f1a0bf1abac..4aecfd69816 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -267,7 +267,7 @@ def gen(self, i=0): x[i] = 1 return self.element_class(self, x) - def gens(self): + def gens(self) -> tuple: """ Return the generators for the group. diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index a856f2120fe..c0a90f0c110 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -416,7 +416,7 @@ def __init__(self, cover, rels, gens): AdditiveAbelianGroup_class.__init__(self, cover, rels) self._orig_gens = tuple(self(x) for x in gens) - def gens(self): + def gens(self) -> tuple: r""" Return the specified generators for self (as a tuple). Compare ``self.smithform_gens()``. diff --git a/src/sage/groups/cactus_group.py b/src/sage/groups/cactus_group.py index a12fbb88563..a4a6fe629b0 100644 --- a/src/sage/groups/cactus_group.py +++ b/src/sage/groups/cactus_group.py @@ -218,7 +218,7 @@ def group_generators(self): return Family(l, lambda x: self.element_class(self, [x])) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` as a tuple. @@ -943,7 +943,7 @@ def gen(self, i): return self.gens()[i] @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/groups/kernel_subgroup.py b/src/sage/groups/kernel_subgroup.py index 605778784e4..dec7a0be573 100644 --- a/src/sage/groups/kernel_subgroup.py +++ b/src/sage/groups/kernel_subgroup.py @@ -69,7 +69,7 @@ def _repr_(self): """ return "Kernel subgroup defined by {}".format(self._morphism) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/groups/lie_gps/nilpotent_lie_group.py b/src/sage/groups/lie_gps/nilpotent_lie_group.py index 19bfb0e2288..1164f28e056 100644 --- a/src/sage/groups/lie_gps/nilpotent_lie_group.py +++ b/src/sage/groups/lie_gps/nilpotent_lie_group.py @@ -310,7 +310,7 @@ def _dRx(self): return R_a.differential(self.one()).matrix().subs(asubs) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return a tuple of elements whose one-parameter subgroups generate the Lie group. diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index ef6ba42a105..afcadc9123a 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -377,7 +377,7 @@ def __init__(self, degree, base_ring, generator_matrices, category=None): MatrixGroup_generic.__init__(self, degree, base_ring, category=category) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of the matrix group. diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index ff7aa0ae9c2..22c44cb2312 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -817,7 +817,7 @@ def algebra_generators(self): from sage.sets.family import Family return Family(V, lambda x: d[x]) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self`` (as an algebra). diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 30e286cea16..419c23cbd70 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -569,7 +569,7 @@ def saturate(self, bound=-1, lower=2): self.__two_descent_data().saturate(bound, lower) self.__saturate = bound - def gens(self): + def gens(self) -> list: """ Return a list of the generators for the Mordell-Weil group. diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py index 0547901b1f1..3fd198b41da 100644 --- a/src/sage/modular/arithgroup/arithgroup_generic.py +++ b/src/sage/modular/arithgroup/arithgroup_generic.py @@ -1028,7 +1028,7 @@ def generators(self, algorithm="farey"): else: raise ValueError("Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm) - def gens(self, *args, **kwds): + def gens(self, *args, **kwds) -> tuple: r""" Return a tuple of generators for this congruence subgroup. diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index ef0af193559..cf6efb9ad8c 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -1017,7 +1017,7 @@ def invariants(self, include_ones=False): self.invariants.set_cache(w, False) return self.invariants(include_ones) - def gens(self): + def gens(self) -> tuple: """ Return tuple of elements `g_0,...,g_n` of ``self`` such that the module generated by the `g_i` is isomorphic to the direct sum of `R/e_i R`, where `e_i` are the diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index ffd6260764d..5b91c5e76c6 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -2513,7 +2513,7 @@ def cardinality(self): return sage.rings.integer.Integer(1) return self.base_ring().cardinality() ** self.rank() - __len__ = cardinality # for backward compatibility + __len__ = cardinality # for backward compatibility def basis(self): """ @@ -2530,7 +2530,7 @@ def basis(self): """ raise NotImplementedError - def gens(self): + def gens(self) -> tuple: """ Return a tuple of basis elements of ``self``. diff --git a/src/sage/modules/quotient_module.py b/src/sage/modules/quotient_module.py index 09c8b313c91..c917188b029 100644 --- a/src/sage/modules/quotient_module.py +++ b/src/sage/modules/quotient_module.py @@ -119,7 +119,7 @@ def __hash__(self): """ return self.__hash - def gens(self): + def gens(self) -> tuple: """ Return the generators of this module. @@ -132,7 +132,7 @@ def gens(self): sage: Q.gens() ((1, 0), (0, 1)) """ - return tuple([self(list(g)) for g in self._module.gens()]) + return tuple(self(list(g)) for g in self._module.gens()) def gen(self, i=0): """ diff --git a/src/sage/modules/submodule.py b/src/sage/modules/submodule.py index 8d00d4f4553..69ec72c33f7 100644 --- a/src/sage/modules/submodule.py +++ b/src/sage/modules/submodule.py @@ -194,7 +194,7 @@ def relations(self): """ return self._ambient.relations() - def gens(self): + def gens(self) -> list: """ Return the generators of this submodule. diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 90d50f0e9a9..d6aea600270 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -520,7 +520,7 @@ def gram_matrix_quadratic(self): G[i, i] = gens[i].q().lift() return G - def gens(self): + def gens(self) -> tuple: r""" Return generators of ``self``. diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 6d1b69b59bb..a4b2465386b 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -265,12 +265,12 @@ def gen(self, i=0): n = self.__ngens if i < 0 or not i < n: raise IndexError(f"argument i (= {i}) must be between 0 and {n-1}") - x = [ 0 for j in range(n) ] + x = [0 for j in range(n)] x[int(i)] = 1 return self.element_class(self, x) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. @@ -280,7 +280,7 @@ def gens(self): sage: F.gens() (a0, a1, a2, a3, a4) """ - return tuple([self.gen(i) for i in range(self.__ngens)]) + return tuple(self.gen(i) for i in range(self.__ngens)) def ngens(self): """ diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py index 31e6c219f89..7af83b5fe4b 100644 --- a/src/sage/monoids/monoid.py +++ b/src/sage/monoids/monoid.py @@ -46,9 +46,9 @@ def __init__(self, names): Parent.__init__(self, base=self, names=names, category=category) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Returns the generators for ``self``. + Return the generators for ``self``. EXAMPLES:: @@ -60,7 +60,7 @@ def gens(self): def monoid_generators(self): r""" - Returns the generators for ``self``. + Return the generators for ``self``. EXAMPLES:: diff --git a/src/sage/quadratic_forms/bqf_class_group.py b/src/sage/quadratic_forms/bqf_class_group.py index af9c9115185..740c85a7ffd 100644 --- a/src/sage/quadratic_forms/bqf_class_group.py +++ b/src/sage/quadratic_forms/bqf_class_group.py @@ -314,7 +314,7 @@ def abelian_group(self): gens = [BinaryQF(g) for g in gens] return AdditiveAbelianGroupWrapper(self, gens, ords) - def gens(self): + def gens(self) -> list: r""" Return a generating set of this form class group. diff --git a/src/sage/quivers/algebra.py b/src/sage/quivers/algebra.py index fee19eb7e93..98e05593177 100644 --- a/src/sage/quivers/algebra.py +++ b/src/sage/quivers/algebra.py @@ -172,7 +172,7 @@ def __init__(self, k, P, order="negdegrevlex"): bracket=False) self._assign_names(self._semigroup.variable_names()) - def order_string(self): + def order_string(self) -> str: """ Return the string that defines the monomial order of this algebra. @@ -195,7 +195,7 @@ def order_string(self): return self._ordstr @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of this algebra (idempotents and arrows). diff --git a/src/sage/quivers/path_semigroup.py b/src/sage/quivers/path_semigroup.py index 85aea6517d0..42ff7b0698e 100644 --- a/src/sage/quivers/path_semigroup.py +++ b/src/sage/quivers/path_semigroup.py @@ -468,7 +468,7 @@ def gen(self, i): return self.gens()[i] @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the tuple of generators. @@ -487,7 +487,7 @@ def gens(self): """ return self.idempotents() + self.arrows() - def is_finite(self): + def is_finite(self) -> bool: """ This partial semigroup is finite if and only if the underlying quiver is acyclic. From 5d6033d4428d6abf9244ba949081e763705ec8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Feb 2024 20:28:55 +0100 Subject: [PATCH 164/191] fix suggested details --- src/sage/modular/abvar/homology.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/modular/abvar/homology.py b/src/sage/modular/abvar/homology.py index 5feb7e3cbce..da586d7c0e0 100644 --- a/src/sage/modular/abvar/homology.py +++ b/src/sage/modular/abvar/homology.py @@ -52,11 +52,11 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.categories.commutative_rings import CommutativeRings from sage.modular.hecke.module import HeckeModule_free_module from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.categories.commutative_rings import CommutativeRings from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal # TODO: we will probably also need homology that is *not* a Hecke module. @@ -74,11 +74,9 @@ def hecke_polynomial(self, n, var='x'): INPUT: + - ``n`` -- positive integer - - ``n`` - positive integer - - - ``var`` - string (default: 'x') the variable name - + - ``var`` -- string (default: 'x') the variable name OUTPUT: a polynomial over ZZ in the given variable From a9e2b743e76eac716dc32b7e55e1a0d23fd66540 Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Thu, 14 Dec 2023 20:41:13 +0530 Subject: [PATCH 165/191] Fixed infinite recursion of the cardinality and order functions In this commit, I have fixed the :trac:`36876` and :trac:`35490`. And I have also added some doc tests covering this changes. --- src/sage/categories/finite_groups.py | 11 +++++++++-- src/sage/groups/matrix_gps/linear.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index 9aa5e0e5fd5..381d19622ee 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -101,12 +101,19 @@ def cardinality(self): sage: G.cardinality() 384 """ + + # Check whether the object has _deg attribute or not + # Note for developers - It will have _deg attribute if it is derived from MatrixGroup_generic class + # And if it has _deg attribute then return the value of that attribute + if hasattr(self, '_deg'): + return self._deg + + # If object does not have _deg attribute then try to find its order with the help of order function. try: o = self.order + return o() except AttributeError: return self._cardinality_from_iterator() - else: - return o() def some_elements(self): """ diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 822bd576afd..fd078ea3401 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -256,6 +256,27 @@ def SL(n, R, var='a'): sage: groups.matrix.SL(2, 3) Special Linear Group of degree 2 over Finite Field of size 3 + + Check if :trac:`36876` is fixed: The cardinality or order of a special linear group with degree=1 over any field is 1:: + + sage: SL(1, QQ).cardinality() + 1 + sage: SL(1, RR).cardinality() + 1 + sage: SL(1, QQ).order() + 1 + sage: SL(1, CC).order() + 1 + + Check if :trac:`35490` is fixed: The special linear group with degree=1 is finite:: + + sage: q = 7 + sage: FqT. = GF(q)[] + sage: N = T^2+1 + sage: FqTN = QuotientRing(FqT, N*FqT) + sage: S = SL(2,FqTN) + sage: S.is_finite() + True """ degree, ring = normalize_args_vectorspace(n, R, var='a') try: From 0101a8d33005f126060ff1a87be7c69ff7a17fbb Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Thu, 14 Dec 2023 20:57:28 +0530 Subject: [PATCH 166/191] Add more doctests to cover all cases Added more doctests. --- src/sage/groups/matrix_gps/linear.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index fd078ea3401..1818bcb082e 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -277,6 +277,18 @@ def SL(n, R, var='a'): sage: S = SL(2,FqTN) sage: S.is_finite() True + + Check if is_finite() function is giving correct answer for the special linear group which is not finite:: + + sage: SL(2, QQ).is_finite() + False + + Check if the cardinality or order of infinite special linear group is +Infinity or not:: + + sage: SL(2, QQ).cardinality() + +Infinity + sage: SL(2, QQ).order() + +Infinity """ degree, ring = normalize_args_vectorspace(n, R, var='a') try: From b0f3988c6833c826a760a1fe133929de7a71ec74 Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Mon, 18 Dec 2023 00:14:46 +0530 Subject: [PATCH 167/191] Corrected lint error --- src/sage/categories/finite_groups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index 381d19622ee..008d650c655 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -102,7 +102,7 @@ def cardinality(self): 384 """ - # Check whether the object has _deg attribute or not + # Check whether the object has _deg attribute or not # Note for developers - It will have _deg attribute if it is derived from MatrixGroup_generic class # And if it has _deg attribute then return the value of that attribute if hasattr(self, '_deg'): From 2dc4a32f7d9d9f15fbc4c3815434f50323c8900a Mon Sep 17 00:00:00 2001 From: Ruchit Jagodara Date: Sat, 23 Dec 2023 18:19:37 +0530 Subject: [PATCH 168/191] Remove unnecessary comments --- src/sage/categories/finite_groups.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index 008d650c655..74841ff9a4b 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -102,13 +102,10 @@ def cardinality(self): 384 """ - # Check whether the object has _deg attribute or not - # Note for developers - It will have _deg attribute if it is derived from MatrixGroup_generic class - # And if it has _deg attribute then return the value of that attribute + # Note for developers - self will have _deg attribute if it is derived from MatrixGroup_generic class if hasattr(self, '_deg'): return self._deg - # If object does not have _deg attribute then try to find its order with the help of order function. try: o = self.order return o() From d4eb8edd99553988679fc8bec2c9d42b30079a4a Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Mon, 25 Dec 2023 14:00:18 +0530 Subject: [PATCH 169/191] Correct the error in finding cardinality of special linear group Corrected the error of previous commit. --- src/sage/categories/finite_groups.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index 74841ff9a4b..f655a2a1a05 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -101,11 +101,10 @@ def cardinality(self): sage: G.cardinality() 384 """ - - # Note for developers - self will have _deg attribute if it is derived from MatrixGroup_generic class - if hasattr(self, '_deg'): - return self._deg - + # If the group is special group and finite then there is only one possibility that the degree + # and cardinality will be one. + if hasattr(self,'_special') and self._special==True: + return 1 try: o = self.order return o() From df64b412d6c4062a9993fbe21245c4533f0ccde9 Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Mon, 25 Dec 2023 20:40:31 +0530 Subject: [PATCH 170/191] Revert all the changes --- src/sage/categories/finite_groups.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index f655a2a1a05..987adbc1cca 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -101,10 +101,6 @@ def cardinality(self): sage: G.cardinality() 384 """ - # If the group is special group and finite then there is only one possibility that the degree - # and cardinality will be one. - if hasattr(self,'_special') and self._special==True: - return 1 try: o = self.order return o() From d15eb8f9cb2727ba132e2228456348c546e488bc Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Mon, 29 Jan 2024 17:40:34 +0530 Subject: [PATCH 171/191] Add a new order method for linear matrix groups --- src/sage/categories/finite_groups.py | 8 +-- src/sage/groups/matrix_gps/linear.py | 83 +++++++++++++++++----------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/sage/categories/finite_groups.py b/src/sage/categories/finite_groups.py index 987adbc1cca..f6866485e93 100644 --- a/src/sage/categories/finite_groups.py +++ b/src/sage/categories/finite_groups.py @@ -101,11 +101,9 @@ def cardinality(self): sage: G.cardinality() 384 """ - try: - o = self.order - return o() - except AttributeError: - return self._cardinality_from_iterator() + if hasattr(self, 'order'): + return self.order() + return self._cardinality_from_iterator() def some_elements(self): """ diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 1818bcb082e..b0a47bbb993 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -256,39 +256,6 @@ def SL(n, R, var='a'): sage: groups.matrix.SL(2, 3) Special Linear Group of degree 2 over Finite Field of size 3 - - Check if :trac:`36876` is fixed: The cardinality or order of a special linear group with degree=1 over any field is 1:: - - sage: SL(1, QQ).cardinality() - 1 - sage: SL(1, RR).cardinality() - 1 - sage: SL(1, QQ).order() - 1 - sage: SL(1, CC).order() - 1 - - Check if :trac:`35490` is fixed: The special linear group with degree=1 is finite:: - - sage: q = 7 - sage: FqT. = GF(q)[] - sage: N = T^2+1 - sage: FqTN = QuotientRing(FqT, N*FqT) - sage: S = SL(2,FqTN) - sage: S.is_finite() - True - - Check if is_finite() function is giving correct answer for the special linear group which is not finite:: - - sage: SL(2, QQ).is_finite() - False - - Check if the cardinality or order of infinite special linear group is +Infinity or not:: - - sage: SL(2, QQ).cardinality() - +Infinity - sage: SL(2, QQ).order() - +Infinity """ degree, ring = normalize_args_vectorspace(n, R, var='a') try: @@ -340,3 +307,53 @@ def _check_matrix(self, x, *args): else: if x.determinant() == 0: raise TypeError('matrix must non-zero determinant') + + def order(self): + """ + Return the order of ``self``. + + EXAMPLES:: + + sage: G = SL(3, GF(5)) + sage: G.order() + 372000 + + TESTS: + + Check if trac:`36876` is fixed:: + sage: SL(1, QQ).order() + 1 + sage: SL(1, ZZ).cardinality() + 1 + + Check if trac:`35490` is fixed:: + sage: q = 7 + ....: FqT. = GF(q)[] + ....: N = T^2+1 + ....: FqTN = QuotientRing(FqT, N*FqT) + ....: S = SL(2,FqTN) + ....: S.is_finite() + True + ....: S.order() + 117600 + """ + from functools import reduce + import operator + from sage.rings.infinity import Infinity + + n = self.degree() + + if self.base_ring().is_finite(): + q = self.base_ring().order() + if self._special: + return reduce(operator.mul, (q**n - q**i for i in range(0, n)), 1) / (q-1) + return reduce(operator.mul, (q**n - q**i for i in range(0, n)), 1) + + if self._special: + if n == 1: + return 1 + return Infinity + + return Infinity + + cardinality = order From e2840edf10d85c797f06d0a3d7376c578697d707 Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Mon, 29 Jan 2024 20:05:53 +0530 Subject: [PATCH 172/191] Corrected a linting error --- src/sage/groups/matrix_gps/linear.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index b0a47bbb993..1f5ae2de321 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -320,13 +320,13 @@ def order(self): TESTS: - Check if trac:`36876` is fixed:: + Check if :trac:`36876` is fixed:: sage: SL(1, QQ).order() 1 sage: SL(1, ZZ).cardinality() 1 - Check if trac:`35490` is fixed:: + Check if :trac:`35490` is fixed:: sage: q = 7 ....: FqT. = GF(q)[] ....: N = T^2+1 From 84077dc31776fcc9e3d6a8da1591c3737254557b Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Sat, 3 Feb 2024 15:40:02 +0530 Subject: [PATCH 173/191] Fixed a doctest --- src/sage/groups/matrix_gps/linear.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 1f5ae2de321..1620394bd7b 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -328,13 +328,13 @@ def order(self): Check if :trac:`35490` is fixed:: sage: q = 7 - ....: FqT. = GF(q)[] - ....: N = T^2+1 - ....: FqTN = QuotientRing(FqT, N*FqT) - ....: S = SL(2,FqTN) - ....: S.is_finite() + sage: FqT. = GF(q)[] + sage: N = T^2+1 + sage: FqTN = QuotientRing(FqT, N*FqT) + sage: S = SL(2,FqTN) + sage: S.is_finite() True - ....: S.order() + sage: S.order() 117600 """ from functools import reduce From a13d56ac8533cf74150f6688bce12cf4e2550a46 Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Thu, 8 Feb 2024 15:42:54 +0530 Subject: [PATCH 174/191] Fixed few linting errors --- src/sage/groups/matrix_gps/linear.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 1620394bd7b..a494e7fdd98 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -323,36 +323,34 @@ def order(self): Check if :trac:`36876` is fixed:: sage: SL(1, QQ).order() 1 - sage: SL(1, ZZ).cardinality() - 1 + sage: SL(2, ZZ).cardinality() + +Infinity Check if :trac:`35490` is fixed:: sage: q = 7 sage: FqT. = GF(q)[] sage: N = T^2+1 sage: FqTN = QuotientRing(FqT, N*FqT) - sage: S = SL(2,FqTN) + sage: S = SL(2, FqTN) sage: S.is_finite() True sage: S.order() 117600 """ - from functools import reduce - import operator from sage.rings.infinity import Infinity + from sage.all import prod n = self.degree() if self.base_ring().is_finite(): q = self.base_ring().order() + ord = prod(q**n - q**i for i in range(n)) / (q-1) if self._special: - return reduce(operator.mul, (q**n - q**i for i in range(0, n)), 1) / (q-1) - return reduce(operator.mul, (q**n - q**i for i in range(0, n)), 1) + return ord / (q-1) + return ord - if self._special: - if n == 1: - return 1 - return Infinity + if self._special and n == 1: + return 1 return Infinity From 1781ad38eda3cb811efbf8806b69b2732c991d5c Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Thu, 8 Feb 2024 18:18:33 +0530 Subject: [PATCH 175/191] Improved coding style --- src/sage/groups/matrix_gps/linear.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index a494e7fdd98..23e2d154e9e 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -344,14 +344,13 @@ def order(self): if self.base_ring().is_finite(): q = self.base_ring().order() - ord = prod(q**n - q**i for i in range(n)) / (q-1) + ord = prod(q**n - q**i for i in range(n)) if self._special: return ord / (q-1) return ord if self._special and n == 1: return 1 - return Infinity cardinality = order From 4b7bac3df96d67ebf9ba659e8b534a54d6150295 Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Fri, 9 Feb 2024 15:04:31 +0530 Subject: [PATCH 176/191] Shifted the position of import statements --- src/sage/groups/matrix_gps/linear.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 23e2d154e9e..70caaf80e26 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -66,6 +66,8 @@ from sage.groups.matrix_gps.named_group import ( normalize_args_vectorspace, NamedMatrixGroup_generic) from sage.misc.latex import latex +from sage.misc.misc_c import prod +from sage.rings.infinity import Infinity ############################################################################### @@ -321,12 +323,14 @@ def order(self): TESTS: Check if :trac:`36876` is fixed:: + sage: SL(1, QQ).order() 1 sage: SL(2, ZZ).cardinality() +Infinity Check if :trac:`35490` is fixed:: + sage: q = 7 sage: FqT. = GF(q)[] sage: N = T^2+1 @@ -337,9 +341,6 @@ def order(self): sage: S.order() 117600 """ - from sage.rings.infinity import Infinity - from sage.all import prod - n = self.degree() if self.base_ring().is_finite(): From baa3b50eb6f9dde726157635200d01d83b95066b Mon Sep 17 00:00:00 2001 From: RuchitJagodara Date: Fri, 9 Feb 2024 15:12:27 +0530 Subject: [PATCH 177/191] Fixed documentation of minimal_normal_subgroups function --- src/sage/groups/libgap_wrapper.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index d2305a655b4..354882f6829 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -347,7 +347,7 @@ class ParentLibGAP(SageObject): def minimal_normal_subgroups(self): """ - Return the nontrivial minimal normal subgroups ``self``. + Return the nontrivial minimal normal subgroups of ``self``. EXAMPLES:: From e3b61195711ae2a8180c29bbfd65db4f15cd6cb9 Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Fri, 9 Feb 2024 13:31:28 +0200 Subject: [PATCH 178/191] Edit unpickling.pyx imports --- src/sage/matroids/unpickling.pyx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx index c791c3aa660..2cd946d7503 100644 --- a/src/sage/matroids/unpickling.pyx +++ b/src/sage/matroids/unpickling.pyx @@ -26,22 +26,19 @@ AUTHORS: # **************************************************************************** from sage.data_structures.bitset_base cimport * -import sage.matroids.matroid -import sage.matroids.basis_exchange_matroid +from sage.libs.gmp.mpq cimport mpq_set +from sage.rings.rational cimport Rational + from sage.matroids.basis_matroid cimport BasisMatroid -from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid from sage.matroids.circuits_matroid cimport CircuitsMatroid +from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid from sage.matroids.dual_matroid import DualMatroid -from sage.matroids.lean_matrix cimport GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, PlusMinusOneMatrix, RationalMatrix from sage.matroids.graphic_matroid import GraphicMatroid +from sage.matroids.lean_matrix cimport GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, PlusMinusOneMatrix, RationalMatrix from sage.matroids.linear_matroid cimport LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid from sage.matroids.minor_matroid import MinorMatroid -from sage.rings.rational cimport Rational -from sage.libs.gmp.mpq cimport mpq_set - - ############################################################################# # BasisMatroid ############################################################################# @@ -403,11 +400,11 @@ def unpickle_rational_matrix(version, data): mpq_set(A._entries[i], ( data[2][i]).value) return A + ############################################################################# # LinearMatroid and subclasses ############################################################################# - def unpickle_linear_matroid(version, data): """ Unpickle a LinearMatroid. @@ -688,11 +685,11 @@ def unpickle_minor_matroid(version, data): M.rename(data[3]) return M + ############################################################################# # Graphic Matroids ############################################################################# - def unpickle_graphic_matroid(version, data): """ Unpickle a GraphicMatroid. From 1b0ab3d05d50b015ee6204655df3bd33c0c8e08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Fri, 9 Feb 2024 11:43:11 -0300 Subject: [PATCH 179/191] sage_docbuild: add needs sphinx tags --- src/sage_docbuild/__main__.py | 1 + src/sage_docbuild/builders.py | 1 + src/sage_docbuild/sphinxbuild.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage_docbuild/__main__.py b/src/sage_docbuild/__main__.py index 0d15808a69c..acfcb8392a0 100644 --- a/src/sage_docbuild/__main__.py +++ b/src/sage_docbuild/__main__.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sphinx r""" Sage docbuild main diff --git a/src/sage_docbuild/builders.py b/src/sage_docbuild/builders.py index a43fdda7350..0be43be307f 100644 --- a/src/sage_docbuild/builders.py +++ b/src/sage_docbuild/builders.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sphinx """ Documentation builders diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index 5ae1d2e6b10..6d5823934b8 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# sage.doctest: needs sphinx r""" Sphinx build script From d06cacb219a2f89a4e6451db90c6c34cc1dea053 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Fri, 9 Feb 2024 18:59:33 +0100 Subject: [PATCH 180/191] enable the generation of acyclic orientations with nauty_directg --- src/sage/graphs/digraph_generators.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index d182f49afb1..1ddad92806c 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -649,14 +649,16 @@ def nauty_directg(self, graphs, options="", debug=False): INPUT: - - ``graphs`` -- a :class:`Graph` or an iterable containing :class:`Graph` - the graph6 string of these graphs is used as an input for ``directg``. + - ``graphs`` -- a :class:`Graph` or an iterable containing + :class:`Graph`. The graph6 string of these graphs is used as an input + for ``directg``. - ``options`` (str) -- a string passed to directg as if it was run at a system command line. Available options from directg --help:: -e | -e: specify a value or range of the total number of arcs -o orient each edge in only one direction, never both + -a only make acyclic orientations (implies -o) -f Use only the subgroup that fixes the first vertices setwise -V only output graphs with nontrivial groups (including exchange of isolated vertices). The -f option is respected. @@ -680,6 +682,19 @@ def nauty_directg(self, graphs, options="", debug=False): sage: len(list(digraphs.nauty_directg(graphs.PetersenGraph(), options="-o"))) 324 + Generate non-isomorphic acyclic orientations:: + + sage: K = graphs.CompleteGraph(4) + sage: all(d.is_directed_acyclic() for d in digraphs.nauty_directg(K, options='-a')) + True + sage: sum(1 for _ in digraphs.nauty_directg(K, options='-a')) + 1 + sage: S = graphs.StarGraph(4) + sage: all(d.is_directed_acyclic() for d in digraphs.nauty_directg(S, options='-a')) + True + sage: sum(1 for _ in digraphs.nauty_directg(S, options='-a')) + 5 + TESTS:: sage: g = digraphs.nauty_directg(graphs.PetersenGraph(), options="-o -G") From 3364def1ded2c7865dcb9b1905dd6afc7489844f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Fri, 9 Feb 2024 10:48:46 -0300 Subject: [PATCH 181/191] Allow python 3.12 --- build/pkgs/python3/spkg-configure.m4 | 2 +- m4/pyproject_toml_metadata.m4 | 2 +- pkgs/sage-setup/pyproject.toml | 2 +- src/setup.cfg.m4 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index 08aad53f36f..bc5baadb94f 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -2,7 +2,7 @@ SAGE_SPKG_CONFIGURE([python3], [ m4_pushdef([MIN_VERSION], [3.9.0]) m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.9.0]) m4_pushdef([LT_STABLE_VERSION], [3.12.0]) - m4_pushdef([LT_VERSION], [3.12.0]) + m4_pushdef([LT_VERSION], [3.13.0]) AC_ARG_WITH([python], [AS_HELP_STRING([--with-python=PYTHON3], [Python 3 executable to use for the Sage venv; default: python3])]) diff --git a/m4/pyproject_toml_metadata.m4 b/m4/pyproject_toml_metadata.m4 index 2511fac45f7..139af7e43e4 100644 --- a/m4/pyproject_toml_metadata.m4 +++ b/m4/pyproject_toml_metadata.m4 @@ -17,4 +17,4 @@ classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", ] urls = {Homepage = "https://www.sagemath.org"} -requires-python = ">=3.9, <3.12" +requires-python = ">=3.9, <3.13" diff --git a/pkgs/sage-setup/pyproject.toml b/pkgs/sage-setup/pyproject.toml index 1499fe4c9ba..4a558643864 100644 --- a/pkgs/sage-setup/pyproject.toml +++ b/pkgs/sage-setup/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", ] urls = {Homepage = "https://www.sagemath.org"} -requires-python = ">=3.9, <3.12" +requires-python = ">=3.9, <3.13" dependencies = [] dynamic = ["version"] diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 23f3d6cb166..e2a3330518d 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -9,7 +9,7 @@ license_files = LICENSE.txt include(`setup_cfg_metadata.m4')dnl' [options] -python_requires = >=3.9, <3.12 +python_requires = >=3.9, <3.13 install_requires = SPKG_INSTALL_REQUIRES_sage_conf SPKG_INSTALL_REQUIRES_six From 71633f36a819de54a05ba98582ff68483e6d30bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Fri, 9 Feb 2024 10:58:28 -0300 Subject: [PATCH 182/191] scipy 1.12 is ok --- build/pkgs/scipy/install-requires.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/scipy/install-requires.txt b/build/pkgs/scipy/install-requires.txt index dd975a870e3..8f07730e208 100644 --- a/build/pkgs/scipy/install-requires.txt +++ b/build/pkgs/scipy/install-requires.txt @@ -4,4 +4,4 @@ # deprecations cannot be introduced in micro releases. # SciPy devs wait "at least 6 months", "in practice two (minor) releases" # from deprecation to removal of a feature. -scipy >=1.5, <1.12 +scipy >=1.5 From 6f243a1368461d489b76c83b95d58fd38d5b9bae Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Feb 2024 12:28:54 -0800 Subject: [PATCH 183/191] .github/workflows/docker.yml: Adjust easimon/maximize-build-space root-reserve-mb for new GitHub runners --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 55b76a99523..1ee938339b3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -155,7 +155,7 @@ jobs: uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images - root-reserve-mb: 40000 + root-reserve-mb: 30000 remove-dotnet: true remove-android: true remove-haskell: true From ea5003b879e3d666136c88e58bfa856194eb9f9a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Feb 2024 22:24:05 -0800 Subject: [PATCH 184/191] .github/workflows/docker_hub.yml: Adjust easimon/maximize-build-space root-reserve-mb for new GitHub runners --- .github/workflows/docker_hub.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_hub.yml b/.github/workflows/docker_hub.yml index b71eec8178d..b1c5f42b77d 100644 --- a/.github/workflows/docker_hub.yml +++ b/.github/workflows/docker_hub.yml @@ -19,7 +19,7 @@ jobs: uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images - root-reserve-mb: 40000 + root-reserve-mb: 30000 remove-dotnet: true remove-android: true remove-haskell: true From 998535fc3ce539f5c14ba051483312d00f6c99e0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Feb 2024 23:14:02 -0800 Subject: [PATCH 185/191] ./sage -fixdoctests --only-tags src/sage/rings/finite_rings/integer_mod_ring.py --- src/sage/rings/finite_rings/integer_mod_ring.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index eb35543a20e..7e300b6ab0c 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -616,14 +616,14 @@ def multiplicative_subgroups(self): EXAMPLES:: - sage: # needs sage.groups - sage: Integers(5).multiplicative_subgroups() # optional - gap_package_polycyclic + sage: # optional - gap_package_polycyclic, needs sage.groups + sage: Integers(5).multiplicative_subgroups() ((2,), (4,), ()) - sage: Integers(15).multiplicative_subgroups() # optional - gap_package_polycyclic + sage: Integers(15).multiplicative_subgroups() ((11, 7), (11, 4), (2,), (11,), (14,), (7,), (4,), ()) - sage: Integers(2).multiplicative_subgroups() # optional - gap_package_polycyclic + sage: Integers(2).multiplicative_subgroups() ((),) - sage: len(Integers(341).multiplicative_subgroups()) # optional - gap_package_polycyclic + sage: len(Integers(341).multiplicative_subgroups()) 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 # optional - gap_package_polycyclic + sage: IntegerModRing(3).multiplicative_subgroups() # optional - gap_package_polycyclic, needs sage.groups ((2,), ()) """ return tuple(tuple(g.value() for g in H.gens()) From 577b3bd282db0f3aa429cacf8818828702deb9bb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Feb 2024 23:17:55 -0800 Subject: [PATCH 186/191] Fix some doctest dataflow warnings --- src/sage/rings/generic.py | 2 +- src/sage/rings/polynomial/polynomial_rational_flint.pyx | 4 ++-- src/sage/schemes/curves/affine_curve.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/generic.py b/src/sage/rings/generic.py index e0145c8e828..bfcb1d2bc50 100644 --- a/src/sage/rings/generic.py +++ b/src/sage/rings/generic.py @@ -36,7 +36,7 @@ class ProductTree: Similarly, the :meth:`interpolation` method can be used to implement the inverse Fast Fourier Transform:: - sage: tree.interpolation(zs).padded_list(len(ys)) == ys + sage: tree.interpolation(zs).padded_list(len(ys)) == ys # needs sage.rings.finite_rings True This class encodes the tree as *layers*: Layer `0` is just a tuple diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 8dd23a143a1..1ef3cbaf681 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2127,11 +2127,11 @@ cdef class Polynomial_rational_flint(Polynomial): :: - sage: # needs sage.libs.pari + sage: # needs sage.groups sage.libs.pari sage: f = x^4 - 17*x^3 - 2*x + 1 sage: G = f.galois_group(pari_group=True); G PARI group [24, -1, 5, "S4"] of degree 4 - sage: PermutationGroup(G) # needs sage.groups + sage: PermutationGroup(G) Transitive group number 5 of degree 4 You can use KASH or GAP to compute Galois groups as well. The advantage is diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index d5d97e5e044..d5ea150f98d 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1801,6 +1801,7 @@ def fundamental_group(self, simplified=True, puiseux=False): to the algebraic field:: sage: # needs sage.rings.number_field + sage: x = polygen(ZZ) sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] sage: F = NumberField(a.minpoly(), 'a', embedding=a) sage: F.inject_variables() From cbde6e7f979bdc8c0742073b834ae8153c70f18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Sat, 10 Feb 2024 13:25:31 -0300 Subject: [PATCH 187/191] Document acceptance of system python 3.12 Co-authored-by: Aliaksei Urbanski --- build/pkgs/python3/SPKG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/python3/SPKG.rst b/build/pkgs/python3/SPKG.rst index 8e2cb6f1360..55757b972e6 100644 --- a/build/pkgs/python3/SPKG.rst +++ b/build/pkgs/python3/SPKG.rst @@ -8,7 +8,7 @@ By default, Sage will try to use system's ``python3`` to set up a virtual environment, a.k.a. `venv `_ rather than building a Python 3 installation from scratch. -Sage will accept versions 3.9.x to 3.11.x. +Sage will accept versions 3.9.x to 3.12.x. You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use ``/path/to/python3_binary`` to set up the venv. Note that setting up the venv requires From 929d0e64fa2e1daa6c167992bf271d3ca70339f8 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Sat, 10 Feb 2024 17:51:10 +0000 Subject: [PATCH 188/191] =?UTF-8?q?fix=20(possibly)=20failing=20doctests?= =?UTF-8?q?=20=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index fdbd3228716..26981f506e6 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -619,7 +619,7 @@ def multiplication_by_p_isogeny(self): sage: p = 23 sage: K. = GF(p^3) - sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: E = EllipticCurve(j=K.random_element()) sage: phi = E.multiplication_by_p_isogeny() sage: assert phi.degree() == p**2 sage: P = E.random_element() @@ -1791,13 +1791,7 @@ def twists(self): sage: p = next_prime(randrange(2,100)) sage: e = randrange(1,10) sage: F. = GF((p,e)) - sage: while True: - ....: try: - ....: E = EllipticCurve([F.random_element() for _ in range(5)]) - ....: except ArithmeticError: - ....: pass - ....: else: - ....: break + sage: E = EllipticCurve(j=F.random_element()) sage: twists1 = E.twists() sage: {sum(E1.is_isomorphic(E2) for E2 in twists1) == 1 for E1 in twists1} {True} From 9bfc29aaf1f1a21b779f92eec927d5a8f000c722 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Feb 2024 11:55:53 -0800 Subject: [PATCH 189/191] src/sage/geometry/polyhedron/backend_field.py: Fix # needs --- src/sage/geometry/polyhedron/backend_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/backend_field.py b/src/sage/geometry/polyhedron/backend_field.py index ca89b53b8b0..feda80cb697 100644 --- a/src/sage/geometry/polyhedron/backend_field.py +++ b/src/sage/geometry/polyhedron/backend_field.py @@ -90,7 +90,7 @@ def _is_zero(self, x): sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic sage: p._is_zero(0) # needs sage.rings.number_field sage.symbolic True - sage: p._is_zero(1/100000) # needs sage.rings.number_field + sage: p._is_zero(1/100000) # needs sage.rings.number_field sage.symbolic False """ return x == 0 From 487a02f15aca07e28ea9d5caff6c83b3322e53e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Sat, 10 Feb 2024 18:34:53 -0300 Subject: [PATCH 190/191] sagemath-standard: don't require Cython to create sdist The implementation of sage.misc.package_dir.read_distributions uses the function `Cython.Utils.open_source_file()` to open a file instead of `io.open()`. This function was introduced in #29701 (b582789). The only difference between `open_source_file()` and `io.open()` is that the former will open the file as binary to check the encoding. But all our files should be utf-8 so we don't need this trick. --- src/sage/misc/package_dir.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 7d0e14cf799..16c78d18462 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -118,8 +118,7 @@ def read_distribution(src_file): sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'modular_decomposition.py')) '' """ - from Cython.Utils import open_source_file - with open_source_file(src_file, error_handling='ignore') as fh: + with open(src_file, encoding='utf-8', errors='ignore') as fh: for line in fh: # Adapted from Cython's Build/Dependencies.py line = line.lstrip() From 30b3d78fac8d4a015ad2641cad44c3d530f4c10c Mon Sep 17 00:00:00 2001 From: Release Manager Date: Wed, 14 Feb 2024 00:20:42 +0100 Subject: [PATCH 191/191] Updated SageMath version to 10.3.beta8 --- CITATION.cff | 4 ++-- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_bliss/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_coxeter3/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_mcqd/install-requires.txt | 2 +- build/pkgs/sagemath_meataxe/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- build/pkgs/sagemath_repl/install-requires.txt | 2 +- build/pkgs/sagemath_sirocco/install-requires.txt | 2 +- build/pkgs/sagemath_tdlib/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_conda/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-bliss/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-coxeter3/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-mcqd/VERSION.txt | 2 +- pkgs/sagemath-meataxe/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- pkgs/sagemath-sirocco/VERSION.txt | 2 +- pkgs/sagemath-tdlib/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 38 files changed, 45 insertions(+), 45 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 4ea68a60cc9..5cf526828d0 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.beta7 +version: 10.3.beta8 doi: 10.5281/zenodo.593563 -date-released: 2024-02-02 +date-released: 2024-02-13 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/VERSION.txt b/VERSION.txt index 1e25d75be11..dc3b1773876 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.3.beta7, Release Date: 2024-02-02 +SageMath version 10.3.beta8, Release Date: 2024-02-13 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index ff4ba21465f..753442bc6aa 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=6517328415e972b571db0ef97c780ae45bb90745 -md5=671ce9d8afcf9ad5faa6a918d7ab2829 -cksum=2336342933 +sha1=0c93ffbc40ee55c429b82c6070817c0133a05013 +md5=96e8d0c3aa48cab18b60499c4d303a73 +cksum=2523075193 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 1733322a267..67d54a29923 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -8dd853eddb146b36c5ce10362ac9b4ca5faf622b +81a374dc4dacba71cf071f981d496ff63b2b2acb diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index 6e88cf9d1a0..0ec273543e2 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.3b7 +sage-conf ~= 10.3b8 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index db8cdd83e08..56e6e3f3495 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.3b7 +sage-docbuild ~= 10.3b8 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index e06c5137c37..f9408e28398 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.3b7 +sage-setup ~= 10.3b8 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 55539104399..9c0f33a7153 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.3b7 +sage-sws2rst ~= 10.3b8 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 9e9032a0594..86712dcb467 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.3b7 +sagemath-standard ~= 10.3b8 diff --git a/build/pkgs/sagemath_bliss/install-requires.txt b/build/pkgs/sagemath_bliss/install-requires.txt index d68ee5fa72e..8da9922be3a 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.3b7 +sagemath-bliss ~= 10.3b8 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 9e721757460..e3ee3bb17c6 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.3b7 +sagemath-categories ~= 10.3b8 diff --git a/build/pkgs/sagemath_coxeter3/install-requires.txt b/build/pkgs/sagemath_coxeter3/install-requires.txt index c24c8e93a3e..a41ca7949f5 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.3b7 +sagemath-coxeter3 ~= 10.3b8 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 6eef4df1e21..738c3d6f68c 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.3b7 +sagemath-environment ~= 10.3b8 diff --git a/build/pkgs/sagemath_mcqd/install-requires.txt b/build/pkgs/sagemath_mcqd/install-requires.txt index 120f5137fc8..fb6b95fb1bb 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.3b7 +sagemath-mcqd ~= 10.3b8 diff --git a/build/pkgs/sagemath_meataxe/install-requires.txt b/build/pkgs/sagemath_meataxe/install-requires.txt index af57d27f60e..12843b536a9 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.3b7 +sagemath-meataxe ~= 10.3b8 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index c90f5dfbded..8cbc66818b8 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.3b7 +sagemath-objects ~= 10.3b8 diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt index 1c63ba6a4e1..57ee651b95f 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.3b7 +sagemath-repl ~= 10.3b8 diff --git a/build/pkgs/sagemath_sirocco/install-requires.txt b/build/pkgs/sagemath_sirocco/install-requires.txt index 168177f8995..cdda9fc7b51 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.3b7 +sagemath-sirocco ~= 10.3b8 diff --git a/build/pkgs/sagemath_tdlib/install-requires.txt b/build/pkgs/sagemath_tdlib/install-requires.txt index baddf839ed1..74ad7694e5f 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.3b7 +sagemath-tdlib ~= 10.3b8 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/src/VERSION.txt b/src/VERSION.txt index 515a2e8e08f..8acf30efc60 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.3.beta7 +10.3.beta8 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index d7b4956c927..de83cdeaae1 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.beta7' -SAGE_RELEASE_DATE='2024-02-02' -SAGE_VERSION_BANNER='SageMath version 10.3.beta7, Release Date: 2024-02-02' +SAGE_VERSION='10.3.beta8' +SAGE_RELEASE_DATE='2024-02-13' +SAGE_VERSION_BANNER='SageMath version 10.3.beta8, Release Date: 2024-02-13' diff --git a/src/sage/version.py b/src/sage/version.py index 9864c02bde4..3662aa06873 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.beta7' -date = '2024-02-02' -banner = 'SageMath version 10.3.beta7, Release Date: 2024-02-02' +version = '10.3.beta8' +date = '2024-02-13' +banner = 'SageMath version 10.3.beta8, Release Date: 2024-02-13'