From 9a1161fdad1a85e0035b70ec5aec218c816deeee Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Mon, 17 Feb 2020 21:30:52 +0100 Subject: [PATCH 001/379] 29243: implement generalized eigenvalues and eigenvectors over RDF/CDF --- src/sage/matrix/matrix2.pyx | 176 ++++++++++++-- src/sage/matrix/matrix_double_dense.pyx | 300 ++++++++++++++++++++---- 2 files changed, 406 insertions(+), 70 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 12345370348..ac6ad3319da 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -6164,12 +6164,42 @@ cdef class Matrix(Matrix1): right_eigenvectors = eigenvectors_right - def eigenmatrix_left(self): + def eigenmatrix_left(self, other=None): r""" - Return matrices D and P, where D is a diagonal matrix of - eigenvalues and P is the corresponding matrix where the rows are - corresponding eigenvectors (or zero vectors) so that P\*self = - D\*P. + Return matrices `D` and `P`, where `D` is a diagonal matrix of + eigenvalues and the rows of `P` are corresponding eigenvectors + (or zero vectors). + + INPUT: + + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved + + OUTPUT: + + If ``self`` is a square matrix `A`, then the output is a diagonal + matrix `D` and a matrix `P` such that + + .. MATH:: + + P A = D P, + + where the rows of `P` are eigenvectors of `A` and the diagonal entries + of `D` are the corresponding eigenvalues. + + If a matrix `B` is passed as optional argument, the output is a + solution to the generalized eigenvalue problem such that + + .. MATH:: + + P A = D P B. + + The ordinary eigenvalue problem is equivalent to the generalized one if + `B` is the identity matrix. + + The generalized eigenvector decomposition is currently only implemented + for matrices over ``RDF`` and ``CDF``. EXAMPLES:: @@ -6189,14 +6219,14 @@ cdef class Matrix(Matrix1): sage: P*A == D*P True - Because P is invertible, A is diagonalizable. + Because `P` is invertible, `A` is diagonalizable. :: sage: A == (~P)*D*P True - The matrix P may contain zero rows corresponding to eigenvalues for + The matrix `P` may contain zero rows corresponding to eigenvalues for which the algebraic multiplicity is greater than the geometric multiplicity. In these cases, the matrix is not diagonalizable. @@ -6206,7 +6236,6 @@ cdef class Matrix(Matrix1): [2 1 0] [0 2 1] [0 0 2] - sage: A = jordan_block(2,3) sage: D, P = A.eigenmatrix_left() sage: D [2 0 0] @@ -6219,6 +6248,42 @@ cdef class Matrix(Matrix1): sage: P*A == D*P True + A generalized eigenvector decomposition:: + + sage: A = matrix(RDF, [[1, -2], [3, 4]]) + sage: B = matrix(RDF, [[0, 7], [2, -3]]) + sage: D, P = A.eigenmatrix_left(B) + sage: (P * A - D * P * B).norm() < 1e-14 + True + + The matrix `B` in a generalized eigenvalue problem may be singular:: + + sage: A = matrix.identity(CDF, 2) + sage: B = matrix(CDF, [[2, 1+I], [4, 2+2*I]]) + sage: D, P = A.eigenmatrix_left(B) + sage: D.diagonal() # tol 1e-14 + [0.19999999999999996 - 0.09999999999999995*I, +infinity] + + In this case, we can still verify the eigenvector equation for the + first eigenvalue and first eigenvector:: + + sage: l = D[0, 0] + sage: v = P[0, :] + sage: (v * A - l * v * B).norm() < 1e-14 + True + + The second eigenvector is contained in the left kernel of `B`:: + + sage: (P[1, :] * B).norm() < 1e-14 + True + + .. SEEALSO:: + + :meth:`eigenvalues`, + :meth:`eigenvectors_left`, + :meth:`.Matrix_double_dense.eigenvectors_left`, + :meth:`eigenmatrix_right`. + TESTS: For matrices with floating point entries, some platforms will @@ -6262,7 +6327,16 @@ cdef class Matrix(Matrix1): """ from sage.misc.flatten import flatten from sage.matrix.constructor import diagonal_matrix, matrix - evecs = self.eigenvectors_left() + if other is None: + evecs = self.eigenvectors_left() + else: + try: + evecs = self.eigenvectors_left(other=other) + except TypeError as e: + raise NotImplementedError('generalized eigenvector ' + 'decomposition is implemented ' + 'for RDF and CDF, but not for %s' + % self.base_ring()) from e D = diagonal_matrix(flatten([[e[0]]*e[2] for e in evecs])) rows = [] for e in evecs: @@ -6272,12 +6346,42 @@ cdef class Matrix(Matrix1): left_eigenmatrix = eigenmatrix_left - def eigenmatrix_right(self): + def eigenmatrix_right(self, other=None): r""" - Return matrices D and P, where D is a diagonal matrix of - eigenvalues and P is the corresponding matrix where the columns are - corresponding eigenvectors (or zero vectors) so that self\*P = - P\*D. + Return matrices `D` and `P`, where `D` is a diagonal matrix of + eigenvalues and the columns of `P` are corresponding eigenvectors + (or zero vectors). + + INPUT: + + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved + + OUTPUT: + + If ``self`` is a square matrix `A`, then the output is a diagonal + matrix `D` and a matrix `P` such that + + .. MATH:: + + A P = P D, + + where the columns of `P` are eigenvectors of `A` and the diagonal + entries of `D` are the corresponding eigenvalues. + + If a matrix `B` is passed as optional argument, the output is a + solution to the generalized eigenvalue problem such that + + .. MATH:: + + A P = B P D. + + The ordinary eigenvalue problem is equivalent to the generalized one if + `B` is the identity matrix. + + The generalized eigenvector decomposition is currently only implemented + for matrices over ``RDF`` and ``CDF``. EXAMPLES:: @@ -6297,14 +6401,14 @@ cdef class Matrix(Matrix1): sage: A*P == P*D True - Because P is invertible, A is diagonalizable. + Because `P` is invertible, `A` is diagonalizable. :: sage: A == P*D*(~P) True - The matrix P may contain zero columns corresponding to eigenvalues + The matrix `P` may contain zero columns corresponding to eigenvalues for which the algebraic multiplicity is greater than the geometric multiplicity. In these cases, the matrix is not diagonalizable. @@ -6314,7 +6418,6 @@ cdef class Matrix(Matrix1): [2 1 0] [0 2 1] [0 0 2] - sage: A = jordan_block(2,3) sage: D, P = A.eigenmatrix_right() sage: D [2 0 0] @@ -6327,6 +6430,42 @@ cdef class Matrix(Matrix1): sage: A*P == P*D True + A generalized eigenvector decomposition:: + + sage: A = matrix(RDF, [[1, -2], [3, 4]]) + sage: B = matrix(RDF, [[0, 7], [2, -3]]) + sage: D, P = A.eigenmatrix_right(B) + sage: (A * P - B * P * D).norm() < 1e-14 + True + + The matrix `B` in a generalized eigenvalue problem may be singular:: + + sage: A = matrix.identity(RDF, 2) + sage: B = matrix(RDF, [[3, 5], [6, 10]]) + sage: D, P = A.eigenmatrix_right(B); D # tol 1e-14 + [0.07692307692307694 0.0] + [ 0.0 +infinity] + + In this case, we can still verify the eigenvector equation for the + first eigenvalue and first eigenvector:: + + sage: l = D[0, 0] + sage: v = P[:, 0] + sage: (A * v - B * v * l).norm() < 1e-14 + True + + The second eigenvector is contained in the right kernel of `B`:: + + sage: (B * P[:, 1]).norm() < 1e-14 + True + + .. SEEALSO:: + + :meth:`eigenvalues`, + :meth:`eigenvectors_right`, + :meth:`.Matrix_double_dense.eigenvectors_right`, + :meth:`eigenmatrix_left`. + TESTS: For matrices with floating point entries, some platforms will @@ -6369,7 +6508,8 @@ cdef class Matrix(Matrix1): True """ - D,P = self.transpose().eigenmatrix_left() + D,P = self.transpose().eigenmatrix_left(None if other is None + else other.transpose()) return D,P.transpose() right_eigenmatrix = eigenmatrix_right diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 99c100bbd9c..35c343c76cf 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -44,6 +44,7 @@ TESTS:: # **************************************************************************** import math +from six import string_types import sage.rings.real_double import sage.rings.complex_double @@ -1204,14 +1205,20 @@ cdef class Matrix_double_dense(Matrix_dense): self.cache('PLU_factors', PLU) return PLU - def eigenvalues(self, algorithm='default', tol=None): + def eigenvalues(self, other=None, algorithm='default', tol=None, + homogeneous=False): r""" - Return a list of eigenvalues. + Return a list of ordinary or generalized eigenvalues. INPUT: - ``self`` - a square matrix + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved; if ``algorithm`` is ``'symmetric'`` or ``'hermitian'``, `B` + must be real symmetric or hermitian positive definite, respectively + - ``algorithm`` - default: ``'default'`` - ``'default'`` - applicable to any matrix @@ -1232,10 +1239,15 @@ cdef class Matrix_double_dense(Matrix_dense): This algorithm can be significantly faster than the ``'default'`` algorithm. - - ``'tol'`` - default: ``None`` - if set to a value other than - ``None`` this is interpreted as a small real number used to aid in - grouping eigenvalues that are numerically similar. See the output - description for more information. + - ``'tol'`` -- (default: ``None``); if set to a value other than + ``None``, this is interpreted as a small real number used to aid in + grouping eigenvalues that are numerically similar, but is ignored + when ``homogeneous`` is set. See the output description for more + information. + + - ``homogeneous`` -- boolean (default: ``False``); if ``True``, use + homogeneous coordinates for the output + (see :meth:`eigenvectors_right` for details) .. WARNING:: @@ -1297,7 +1309,7 @@ cdef class Matrix_double_dense(Matrix_dense): sage: ev[0].parent() Real Double Field - The matrix ``A`` is "random", but the construction of ``B`` + The matrix ``A`` is "random", but the construction of ``C`` provides a positive-definite Hermitian matrix. Note that the eigenvalues of a Hermitian matrix are real, and the eigenvalues of a positive-definite matrix will be positive. :: @@ -1306,8 +1318,8 @@ cdef class Matrix_double_dense(Matrix_dense): ....: [ 7*I - 2, -4*I + 7, -2*I + 4, 8*I + 8], ....: [-2*I + 1, 6*I + 6, 5*I + 5, -I - 4], ....: [ 5*I + 1, 6*I + 2, I - 4, -I + 3]]) - sage: B = (A*A.conjugate_transpose()).change_ring(CDF) - sage: ev = B.eigenvalues(algorithm='hermitian'); ev + sage: C = (A*A.conjugate_transpose()).change_ring(CDF) + sage: ev = C.eigenvalues(algorithm='hermitian'); ev [2.68144025..., 49.5167998..., 274.086188..., 390.71557...] sage: ev[0].parent() Real Double Field @@ -1335,6 +1347,25 @@ cdef class Matrix_double_dense(Matrix_dense): sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5) # tol 2e-15 [(-8.0, 22), (1.9999999999999984, 77), (21.999999999999996, 1)] + In this generalized eigenvalue problem, the homogeneous coordinates + explain the output obtained for the eigenvalues:: + + sage: A = matrix.identity(RDF, 2) + sage: B = matrix(RDF, [[3, 5], [6, 10]]) + sage: A.eigenvalues(B) # tol 1e-14 + [0.0769230769230769, +infinity] + sage: E = A.eigenvalues(B, homogeneous=True); E # random + [(0.9999999999999999, 13.000000000000002), (0.9999999999999999, 0.0)] + sage: [alpha/beta for alpha, beta in E] # tol 1e-14 + [0.0769230769230769, NaN + NaN*I] + + .. SEEALSO:: + + :meth:`eigenvectors_left`, + :meth:`eigenvectors_right`, + :meth:`.Matrix.eigenmatrix_left`, + :meth:`.Matrix.eigenmatrix_right`. + TESTS: Testing bad input. :: @@ -1373,23 +1404,43 @@ cdef class Matrix_double_dense(Matrix_dense): sage: matrix(CDF,0,0).eigenvalues() [] + + Check that homogeneous coordinates work for hermitian positive definite + input:: + + sage: A = matrix.identity(CDF, 2) + sage: B = matrix(CDF, [[2, 1+I], [1-I, 3]]) + sage: A.eigenvalues(B, algorithm='hermitian', homogeneous=True) # tol 1e-14 + [(0.25, 1.0), (0.9999999999999998, 1.0)] """ - import sage.rings.real_double - import sage.rings.complex_double - import numpy + from sage.rings.real_double import RDF + from sage.rings.complex_double import CDF + if isinstance(other, string_types): + # for backward compatibilty, allow algorithm to be passed as first + # positional argument + algorithm = other + other = None if not algorithm in ['default', 'symmetric', 'hermitian']: msg = "algorithm must be 'default', 'symmetric', or 'hermitian', not {0}" raise ValueError(msg.format(algorithm)) - if not self.is_square(): + if not self.is_square() or other is not None and not other.is_square(): msg = 'matrix must be square, not {0} x {1}' - raise ValueError(msg.format(self.nrows(), self.ncols())) - if algorithm == 'symmetric' and self.base_ring() == sage.rings.complex_double.CDF: - try: - self = self.change_ring(sage.rings.real_double.RDF) # check side effect - except TypeError: - raise TypeError('cannot apply symmetric algorithm to matrix with complex entries') + m = self if not self.is_square() else other + raise ValueError(msg.format(m.nrows(), m.ncols())) if algorithm == 'symmetric': + if self.base_ring() != RDF: + try: + self = self.change_ring(RDF) # check side effect + except TypeError: + raise TypeError('cannot apply symmetric algorithm to matrix with complex entries') + if other is not None and other.base_ring() != RDF: + try: + other = other.change_ring(RDF) # check side effect + except TypeError: + raise TypeError('cannot apply symmetric algorithm to matrix with complex entries') algorithm = 'hermitian' + if homogeneous: + tol = None multiplicity = (tol is not None) if multiplicity: try: @@ -1401,12 +1452,6 @@ cdef class Matrix_double_dense(Matrix_dense): msg = 'tolerance parameter must be positive, not {0}' raise ValueError(msg.format(tol)) - if self._nrows == 0: - return [] - global scipy - if scipy is None: - import scipy - import scipy.linalg if self._nrows == 0: return [] global scipy @@ -1416,14 +1461,24 @@ cdef class Matrix_double_dense(Matrix_dense): global numpy if numpy is None: import numpy + other_numpy = None if other is None else other.numpy() # generic eigenvalues, or real eigenvalues for Hermitian if algorithm == 'default': - return_class = sage.rings.complex_double.CDF - evalues = scipy.linalg.eigvals(self._matrix_numpy) + return_class = CDF + evalues = scipy.linalg.eigvals(self._matrix_numpy, other_numpy, + homogeneous_eigvals=homogeneous) elif algorithm == 'hermitian': - return_class = sage.rings.real_double.RDF - evalues = scipy.linalg.eigh(self._matrix_numpy, eigvals_only=True) - if not multiplicity: + return_class = RDF + evalues = scipy.linalg.eigh(self._matrix_numpy, other_numpy, + eigvals_only=True) + if homogeneous: + # eigh does not support homogeneous output + evalues = evalues, [RDF.one()] * len(evalues) + + if homogeneous: + return [(return_class(a), return_class(b)) + for a, b in zip(*evalues)] + elif not multiplicity: return [return_class(e) for e in evalues] else: # pairs in ev_group are @@ -1449,19 +1504,47 @@ cdef class Matrix_double_dense(Matrix_dense): ev_group[location][2] = ev_group[location][0]/ev_group[location][1] return [(return_class(avg), m) for _, m, avg in ev_group] - def left_eigenvectors(self): + def left_eigenvectors(self, other=None, homogeneous=False): r""" - Compute the left eigenvectors of a matrix of double precision - real or complex numbers (i.e. RDF or CDF). + Compute the ordinary or generalized left eigenvectors of a matrix of + double precision real or complex numbers (i.e. ``RDF`` or ``CDF``). + + INPUT: + + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved + + - ``homogeneous`` -- boolean (default: ``False``); if ``True``, use + homogeneous coordinates for the eigenvalues in the output OUTPUT: - Returns a list of triples, each of the form ``(e,[v],1)``, + A list of triples, each of the form ``(e,[v],1)``, where ``e`` is the eigenvalue, and ``v`` is an associated - left eigenvector. If the matrix is of size `n`, then there are - `n` triples. Values are computed with the SciPy library. + left eigenvector such that + + .. MATH:: + + v A = e v. + + If the matrix `A` is of size `n`, then there are `n` triples. - The format of this output is designed to match the format + If a matrix `B` is passed as optional argument, the output is a + solution to the generalized eigenvalue problem such that + + .. MATH:: + + v A = e v B. + + If ``homogeneous`` is set, each eigenvalue is returned as a tuple + `(\alpha, \beta)` of homogeneous coordinates such that + + .. MATH:: + + \beta v A = \alpha v B. + + The format of the output is designed to match the format for exact results. However, since matrices here have numerical entries, the resulting eigenvalues will also be numerical. No attempt is made to determine if two eigenvalues are equal, or if @@ -1472,8 +1555,13 @@ cdef class Matrix_double_dense(Matrix_dense): The SciPy routines used for these computations produce eigenvectors normalized to have length 1, but on different hardware they may vary - by a sign. So for doctests we have normalized output by forcing their - eigenvectors to have their first non-zero entry equal to one. + by a complex sign. So for doctests we have normalized output by forcing + their eigenvectors to have their first non-zero entry equal to one. + + ALGORITHM: + + Values are computed with the SciPy library using + :func:`scipy:scipy.linalg.eig`. EXAMPLES:: @@ -1495,6 +1583,31 @@ cdef class Matrix_double_dense(Matrix_dense): sage: spectrum[3] # tol 1e-13 (-1.0000000000000018, [(1.0, 0.9999999999999568, 1.9999999999998794, 1.9999999999998472)], 1) + A generalized eigenvalue problem:: + + sage: A = matrix(CDF, [[1+I, -2], [3, 4]]) + sage: B = matrix(CDF, [[0, 7-I], [2, -3]]) + sage: E = A.eigenvectors_left(B) + sage: all((v * A - e * v * B).norm() < 1e-14 for e, [v], _ in E) + True + + In a generalized eigenvalue problem with a singular matrix `B`, we can + check the eigenvector property using homogeneous coordinates, even + though the quotient `\alpha/\beta` is not always defined:: + + sage: A = matrix.identity(CDF, 2) + sage: B = matrix(CDF, [[2, 1+I], [4, 2+2*I]]) + sage: E = A.eigenvectors_left(B, homogeneous=True) + sage: all((beta * v * A - alpha * v * B).norm() < 1e-14 + ....: for (alpha, beta), [v], _ in E) + True + + .. SEEALSO:: + + :meth:`eigenvalues`, + :meth:`eigenvectors_right`, + :meth:`.Matrix.eigenmatrix_left`. + TESTS: The following example shows that :trac:`20439` has been resolved:: @@ -1521,32 +1634,75 @@ cdef class Matrix_double_dense(Matrix_dense): """ if not self.is_square(): raise ArithmeticError("self must be a square matrix") + if other is not None and not other.is_square(): + raise ArithmeticError("other must be a square matrix") if self._nrows == 0: return [], self.__copy__() global scipy if scipy is None: import scipy import scipy.linalg - v,eig = scipy.linalg.eig(self._matrix_numpy, right=False, left=True) + v, eig = scipy.linalg.eig(self._matrix_numpy, + None if other is None else other.numpy(), + right=False, left=True, + homogeneous_eigvals=homogeneous) # scipy puts eigenvectors in columns, we will extract from rows eig = matrix(eig.T) - return [(sage.rings.complex_double.CDF(v[i]), [eig[i].conjugate()], 1) for i in range(len(v))] + if other is not None: + # scipy fails to normalize generalized left eigenvectors + # (see https://github.com/scipy/scipy/issues/11550), + # FIXME: remove this normalization step once that issue is resolved + eig = [v.normalized() for v in eig] + from sage.rings.complex_double import CDF + if homogeneous: + v = [(CDF(a), CDF(b)) for a, b in v.T] + else: + v = [CDF(e) for e in v] + return [(v[i], [eig[i].conjugate()], 1) for i in range(len(v))] eigenvectors_left = left_eigenvectors - def right_eigenvectors(self): + def right_eigenvectors(self, other=None, homogeneous=False): r""" - Compute the right eigenvectors of a matrix of double precision - real or complex numbers (i.e. RDF or CDF). + Compute the ordinary or generalized right eigenvectors of a matrix of + double precision real or complex numbers (i.e. ``RDF`` or ``CDF``). + + INPUT: + + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved + + - ``homogeneous`` -- boolean (default: ``False``); if ``True``, use + homogeneous coordinates for the eigenvalues in the output OUTPUT: - Returns a list of triples, each of the form ``(e,[v],1)``, + A list of triples, each of the form ``(e,[v],1)``, where ``e`` is the eigenvalue, and ``v`` is an associated - right eigenvector. If the matrix is of size `n`, then there - are `n` triples. Values are computed with the SciPy library. + right eigenvector such that + + .. MATH:: + + A v = e v. + + If the matrix `A` is of size `n`, then there are `n` triples. + + If a matrix `B` is passed as optional argument, the output is a + solution to the generalized eigenvalue problem such that - The format of this output is designed to match the format + .. MATH:: + + A v = e B v. + + If ``homogeneous`` is set, each eigenvalue is returned as a tuple + `(\alpha, \beta)` of homogeneous coordinates such that + + .. MATH:: + + \beta A v = \alpha B v. + + The format of the output is designed to match the format for exact results. However, since matrices here have numerical entries, the resulting eigenvalues will also be numerical. No attempt is made to determine if two eigenvalues are equal, or if @@ -1557,8 +1713,13 @@ cdef class Matrix_double_dense(Matrix_dense): The SciPy routines used for these computations produce eigenvectors normalized to have length 1, but on different hardware they may vary - by a sign. So for doctests we have normalized output by forcing their - eigenvectors to have their first non-zero entry equal to one. + by a complex sign. So for doctests we have normalized output by forcing + their eigenvectors to have their first non-zero entry equal to one. + + ALGORITHM: + + Values are computed with the SciPy library using + :func:`scipy:scipy.linalg.eig`. EXAMPLES:: @@ -1580,6 +1741,31 @@ cdef class Matrix_double_dense(Matrix_dense): sage: spectrum[3] # tol 1e-13 (-1.0000000000000406, [(1.0, -0.49999999999996264, 1.9999999999998617, 0.499999999999958)], 1) + A generalized eigenvalue problem:: + + sage: A = matrix(CDF, [[1+I, -2], [3, 4]]) + sage: B = matrix(CDF, [[0, 7-I], [2, -3]]) + sage: E = A.eigenvectors_right(B) + sage: all((A * v - e * B * v).norm() < 1e-14 for e, [v], _ in E) + True + + In a generalized eigenvalue problem with a singular matrix `B`, we can + check the eigenvector property using homogeneous coordinates, even + though the quotient `\alpha/\beta` is not always defined:: + + sage: A = matrix.identity(RDF, 2) + sage: B = matrix(RDF, [[3, 5], [6, 10]]) + sage: E = A.eigenvectors_right(B, homogeneous=True) + sage: all((beta * A * v - alpha * B * v).norm() < 1e-14 + ....: for (alpha, beta), [v], _ in E) + True + + .. SEEALSO:: + + :meth:`eigenvalues`, + :meth:`eigenvectors_left`, + :meth:`.Matrix.eigenmatrix_right`. + TESTS: The following example shows that :trac:`20439` has been resolved:: @@ -1605,16 +1791,26 @@ cdef class Matrix_double_dense(Matrix_dense): """ if not self.is_square(): raise ArithmeticError("self must be a square matrix") + if other is not None and not other.is_square(): + raise ArithmeticError("other must be a square matrix") if self._nrows == 0: return [], self.__copy__() global scipy if scipy is None: import scipy import scipy.linalg - v,eig = scipy.linalg.eig(self._matrix_numpy, right=True, left=False) + v, eig = scipy.linalg.eig(self._matrix_numpy, + None if other is None else other.numpy(), + right=True, left=False, + homogeneous_eigvals=homogeneous) # scipy puts eigenvectors in columns, we will extract from rows eig = matrix(eig.T) - return [(sage.rings.complex_double.CDF(v[i]), [eig[i]], 1) for i in range(len(v))] + from sage.rings.complex_double import CDF + if homogeneous: + v = [(CDF(a), CDF(b)) for a, b in v.T] + else: + v = [CDF(e) for e in v] + return [(v[i], [eig[i]], 1) for i in range(len(v))] eigenvectors_right = right_eigenvectors From fab67c99e341b6cf27829d2b77e5e761c1fd6ab4 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Mon, 24 Feb 2020 22:20:19 +0100 Subject: [PATCH 002/379] 29243: decrease accuracy in doctest output --- src/sage/matrix/matrix2.pyx | 2 +- src/sage/matrix/matrix_double_dense.pyx | 26 ++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index ac6ad3319da..c02322847d2 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -6262,7 +6262,7 @@ cdef class Matrix(Matrix1): sage: B = matrix(CDF, [[2, 1+I], [4, 2+2*I]]) sage: D, P = A.eigenmatrix_left(B) sage: D.diagonal() # tol 1e-14 - [0.19999999999999996 - 0.09999999999999995*I, +infinity] + [0.2 - 0.1*I, +infinity] In this case, we can still verify the eigenvector equation for the first eigenvalue and first eigenvector:: diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 35c343c76cf..2b8e45149d5 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -1305,7 +1305,7 @@ cdef class Matrix_double_dense(Matrix_dense): sage: A = graphs.PetersenGraph().adjacency_matrix() sage: A = A.change_ring(RDF) sage: ev = A.eigenvalues(algorithm='symmetric'); ev # tol 1e-14 - [-2.0000000000000004, -1.9999999999999998, -1.9999999999999998, -1.9999999999999993, 0.9999999999999994, 0.9999999999999997, 1.0, 1.0000000000000002, 1.0000000000000004, 2.9999999999999996] + [-2.0, -2.0, -2.0, -2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0] sage: ev[0].parent() Real Double Field @@ -1335,17 +1335,17 @@ cdef class Matrix_double_dense(Matrix_dense): sage: A = G.adjacency_matrix().change_ring(RDF) sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5) # tol 1e-15 - [(-1.9999999999999998, 4), (1.0, 5), (2.9999999999999996, 1)] + [(-2.0, 4), (1.0, 5), (3.0, 1)] sage: A.eigenvalues(algorithm='symmetric', tol=2.5) # tol 1e-15 - [(-1.9999999999999998, 4), (1.3333333333333333, 6)] + [(-2.0, 4), (1.3333333333333333, 6)] An (extreme) example of properly grouping similar eigenvalues. :: sage: G = graphs.HigmanSimsGraph() sage: A = G.adjacency_matrix().change_ring(RDF) sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5) # tol 2e-15 - [(-8.0, 22), (1.9999999999999984, 77), (21.999999999999996, 1)] + [(-8.0, 22), (2.0, 77), (22.0, 1)] In this generalized eigenvalue problem, the homogeneous coordinates explain the output obtained for the eigenvalues:: @@ -1411,7 +1411,7 @@ cdef class Matrix_double_dense(Matrix_dense): sage: A = matrix.identity(CDF, 2) sage: B = matrix(CDF, [[2, 1+I], [1-I, 3]]) sage: A.eigenvalues(B, algorithm='hermitian', homogeneous=True) # tol 1e-14 - [(0.25, 1.0), (0.9999999999999998, 1.0)] + [(0.25, 1.0), (1.0, 1.0)] """ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF @@ -1575,13 +1575,13 @@ cdef class Matrix_double_dense(Matrix_dense): sage: for i in range(len(spectrum)): ....: spectrum[i][1][0] = matrix(RDF, spectrum[i][1]).echelon_form()[0] sage: spectrum[0] # tol 1e-13 - (2.0000000000000675, [(1.0, 1.0000000000000138, 1.0000000000000147, 1.0000000000000309)], 1) + (2.0, [(1.0, 1.0, 1.0, 1.0)], 1) sage: spectrum[1] # tol 1e-13 - (0.9999999999999164, [(0.9999999999999999, 0.7999999999999833, 0.7999999999999836, 0.5999999999999696)], 1) + (1.0, [(1.0, 0.8, 0.8, 0.6)], 1) sage: spectrum[2] # tol 1e-13 - (-1.9999999999999782, [(1.0, 0.40000000000000335, 0.6000000000000039, 0.2000000000000051)], 1) + (-2.0, [(1.0, 0.4, 0.6, 0.2)], 1) sage: spectrum[3] # tol 1e-13 - (-1.0000000000000018, [(1.0, 0.9999999999999568, 1.9999999999998794, 1.9999999999998472)], 1) + (-1.0, [(1.0, 1.0, 2.0, 2.0)], 1) A generalized eigenvalue problem:: @@ -1733,13 +1733,13 @@ cdef class Matrix_double_dense(Matrix_dense): sage: for i in range(len(spectrum)): ....: spectrum[i][1][0] = matrix(RDF, spectrum[i][1]).echelon_form()[0] sage: spectrum[0] # tol 1e-13 - (2.000000000000048, [(1.0, -2.0000000000001523, 3.000000000000181, 1.0000000000000746)], 1) + (2.0, [(1.0, -2.0, 3.0, 1.0)], 1) sage: spectrum[1] # tol 1e-13 - (0.999999999999941, [(1.0, -0.666666666666633, 1.333333333333286, 0.33333333333331555)], 1) + (1.0, [(1.0, -0.666666666666633, 1.333333333333286, 0.33333333333331555)], 1) sage: spectrum[2] # tol 1e-13 - (-1.9999999999999483, [(1.0, -0.2000000000000063, 1.0000000000000173, 0.20000000000000498)], 1) + (-2.0, [(1.0, -0.2, 1.0, 0.2)], 1) sage: spectrum[3] # tol 1e-13 - (-1.0000000000000406, [(1.0, -0.49999999999996264, 1.9999999999998617, 0.499999999999958)], 1) + (-1.0, [(1.0, -0.5, 2.0, 0.5)], 1) A generalized eigenvalue problem:: From a8dc4997962682ff164da76306010b280b941fc2 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Tue, 25 Feb 2020 18:22:38 +0100 Subject: [PATCH 003/379] 29243: remove the use of six in pyx-file --- src/sage/matrix/matrix_double_dense.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 2b8e45149d5..d2caea3bbcd 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -44,7 +44,6 @@ TESTS:: # **************************************************************************** import math -from six import string_types import sage.rings.real_double import sage.rings.complex_double @@ -1415,7 +1414,7 @@ cdef class Matrix_double_dense(Matrix_dense): """ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF - if isinstance(other, string_types): + if isinstance(other, str): # for backward compatibilty, allow algorithm to be passed as first # positional argument algorithm = other From a02c547a9e2ce6432b4a44e0c749d793a59a39ac Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 15 Jun 2020 15:17:24 -0400 Subject: [PATCH 004/379] 29844: initial berkovich implementation --- src/sage/schemes/all.py | 1 + src/sage/schemes/berkovich/__init__.py | 0 src/sage/schemes/berkovich/all.py | 7 + src/sage/schemes/berkovich/berkovich_space.py | 1240 +++++++++++++++++ 4 files changed, 1248 insertions(+) create mode 100644 src/sage/schemes/berkovich/__init__.py create mode 100644 src/sage/schemes/berkovich/all.py create mode 100644 src/sage/schemes/berkovich/berkovich_space.py diff --git a/src/sage/schemes/all.py b/src/sage/schemes/all.py index 62cd7ef3ccf..67b0a96f401 100644 --- a/src/sage/schemes/all.py +++ b/src/sage/schemes/all.py @@ -45,3 +45,4 @@ from .cyclic_covers.all import * +from .berkovich.all import * \ No newline at end of file diff --git a/src/sage/schemes/berkovich/__init__.py b/src/sage/schemes/berkovich/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/schemes/berkovich/all.py b/src/sage/schemes/berkovich/all.py new file mode 100644 index 00000000000..8bc437bdbf6 --- /dev/null +++ b/src/sage/schemes/berkovich/all.py @@ -0,0 +1,7 @@ +"""nodoctest +all.py -- export of projective schemes to Sage +""" +from __future__ import absolute_import + +from .berkovich_space import Berkovich_Cp_Affine, Berkovich_Cp_Projective + diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py new file mode 100644 index 00000000000..6aae9489274 --- /dev/null +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -0,0 +1,1240 @@ +""" +A framework for implementing the Berkovich construction over a scheme. +""" + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element import Element +from sage.categories.topological_spaces import TopologicalSpaces +from sage.symbolic.expression import is_Expression +from sage.rings.real_mpfr import RR +from sage.rings.padics.padic_generic_element import pAdicGenericElement +from sage.rings.padics.factory import Qp +from sage.schemes.projective.projective_space import ProjectiveSpace +from sage.schemes.projective.projective_point import SchemeMorphism_point_projective_field +from sage.schemes.generic.morphism import is_SchemeMorphism +from sage.schemes.affine.affine_space import AffineSpace +from sage.schemes.generic.scheme import Scheme +from sage.rings.rational_field import QQ +from sage.rings.real_mpfr import is_RealNumber + + +class Berkovich_Element(Element): + """ + The parent class for any element of a Berkovich space + """ + pass + +class Berkovich_Element_Cp(Berkovich_Element): + """ + The parent class for any element of Berkovich space over ``Cp``. + This class should never be instantiated, instead BerkSpaceCpAffineElement + or BerkSpaceCpProjective should be used. + """ + + def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, error_check=True): + Element.__init__(self, parent) + from sage.rings.function_field.element import is_FunctionFieldElement + from sage.rings.polynomial.polynomial_element import is_Polynomial + from sage.rings.fraction_field_element import FractionFieldElement_1poly_field + self._type = None + if isinstance(center, list): + if error_check: + if not isinstance(radius, list): + raise ValueError("center was passed a list but radius was not a list") + if len(radius) != len(center): + raise ValueError("The same number of centers and radii must be specified to create a Type IV point") + self._center_lst = list(center) + self._radius_lst = list(radius) + self._prec = len(self._radius_lst) + self._center_func = None + self._radius_func = None + self._type = 4 + if not error_check: + return + elif hasattr(center, "parent"): + if is_FunctionFieldElement(center) or is_Polynomial(center) or isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): + #check that both center and radii are supported univariate function + center_expr_check = False + radius_expr_check = False + if error_check: + if is_Expression(center): + if len(center.variables()) != 1: + raise ValueError("An expression with %s variables cannot define the centers approximating a Type IV point" %(len(center.variables()))) + else: + center_expr_check = True + if not (is_FunctionFieldElement(radius) or is_Polynomial(radius) or isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius)): + raise ValueError("center was passed a function but radius was not a function") + if is_Expression(radius): + if len(radius.variables()) != 1: + raise ValueError("An expression with %s variables cannot define the radii approximating a Type IV point" %(len(radius.variables()))) + else: + radius_expr_check = True + else: + if is_Expression(center): + center_expr_check = True + if is_Expression(radius): + radius_expr_check = True + self._type = 4 + self._prec = prec + center_lst = [] + radius_lst = [] + self._center_func = center + self._radius_func = radius + if center_expr_check: + x = self._center_func.variables()[0] + if radius_expr_check: + y = self._radius_func.variables()[0] + for i in range(1,self._prec+1): + if center_expr_check: + center_lst.append(self._center_func.subs({x:i})) #we use .subs for expressions to avoid deprecation + else: + center_lst.append(self._center_func(i)) #.subs for polynomials is currently bugged + if radius_expr_check: + radius_lst.append(self._radius_func.subs({y:i})) + else: + radius_lst.append(self._radius_func(i)) + self._center_lst = center_lst + self._radius_lst = radius_lst + if not error_check: + return + if self._type == 4 and error_check: + if child == "projective": + for i in range(len(self._center_lst)): + center = self._center_lst[i] + radius = self._radius_lst[i] + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + self._center_lst[i] = center + except: + raise ValueError("The center of a point of Projective Berkovich space must be a point of P^1(Cp(%s))"%(self._p)) + if center.scheme().ambient_space() != center.scheme(): + raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) + if (center.scheme()).base_ring().prime() != self._p: + raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) + if not is_RealNumber(radius): + if is_Expression(radius): + radius = RR(radius) + elif RR.has_coerce_map_from(radius.parent()): + radius = RR(radius) + self._radius_lst[i] = radius + else: + raise ValueError("The radius of a disk approximating a Type IV point must coerce into the real numbers, %s does not coerce" %(radius)) + if i != 0: + previous_center = self._center_lst[i-1] + previous_radius = self._radius_lst[i-1] + dist = (center[0] - previous_center[0]).abs() + if previous_radius < radius or dist > radius: + raise ValueError("Sequence of disks does not define a Type IV point as containment is not proper") + return + elif child == "affine": + for i in range(len(self._center_lst)): + center = self._center_lst[i] + radius = self._radius_lst[i] + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + self._center_lst[i] = center + except: + raise ValueError("The center of a disk approximating a Type IV point must be padic or coerce into Qp, %s does not coerse into Qp" %(center)) + if (center.parent()).prime() != self._p: + raise ValueError("The center of a disk approximating a Type IV point of Berkovich space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + if not is_RealNumber(radius): + if is_Expression(radius): + radius = RR(radius) + elif RR.has_coerce_map_from(radius.parent()): + radius = RR(radius) + self._radius_lst[i] = radius + else: + raise ValueError("The radius of a disk approximating a Type IV point must coerce into the real numbers, %s does not coerce" %(radius)) + if i != 0: + previous_center = self._center_lst[i-1] + previous_radius = self._radius_lst[i-1] + dist = (center - previous_center).abs() + if previous_radius < radius or dist > radius: + raise ValueError("Sequence of disks does not define a Type IV point as containment is not proper") + return + else: + raise ValueError("bad value %s passed to child. Do not initialize BerkSpaceCpElement directly" %(child)) + return + self._prec = None + self._center = center + if (radius == None and power == None) or radius == 0: + self._type = 1 + self._radius = 0 + self._power = None + return + if power != None: + if error_check: + if power.parent() != QQ: + try: + power = QQ(power) + except: + raise ValueError("power must convert to rationals") + if radius != None: + if radius != self._p**power: + raise ValueError("Conflicting inputs for power and radius") + self._power = power + self._radius = RR(self._p**power) + self._type = 2 + return + if radius != None: + if is_Expression(radius): + try: + power = QQ((radius.log(self._p)).expand_log()) + except: + pass + try: + radius = RR(radius) + self._radius = radius + except: + raise ValueError("Symbolic radius must be a real number") + if (not is_RealNumber(radius)) and power == None: + if RR.has_coerce_map_from(radius.parent()): + self._radius = RR(radius) + else: + raise ValueError("Radius must coerce into real numbers") + else: + self._radius = radius + if power != None: + self._power = power + self._type = 2 + return + power = radius.log(self._p) + if power.is_integer(): + self._power = QQ(power) + self._type = 2 + else: + self._type = 3 + self._power = None + + def radius(self): + """ + Radius of the corresponding disk (or sequence of disks) in ``Cp``. + + OUTPUT: + + - For Type I, II, and III points, returns a list of a single non-negative + real number. For Type IV points, returns a list of non-negative real numbers. + """ + if self._type == 4: + return self._radius_lst[:] + return self._radius + + def diameter(self): + """ + Diameter function on Berkovich space + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(3) + sage: Q1.diameter() + 0 + + sage: Q2 = B(1/2,9) + sage: Q2.diameter() + 9.00000000000000 + + """ + if self._type == 4: + if self._radius_func == None: + return self._radius_lst[len(self._radius_lst)-1] + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(QQ,names="x") + x = R.gens()[0] + if is_Expression(self._radius_func): + radius_func_variable = self._radius_func.variables()[0] + radius_expr = self._radius_func.subs({radius_func_variable:x}) + else: + radius_expr = self._radius_func(x) + from sage.symbolic.ring import SymbolicRing as SR + radius_expr = SR(RR)(radius_expr) + return radius_expr.limit(x="oo") + return self._radius + + def hyperbolic_metric(self, other): + """ + Returns the hypebolic distance metric distance between ``self`` and ``other``. + Not defined for Type I points. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(1/4,4) + sage: Q2 = B(1/4,6) + sage: Q1.hyperbolic_metric(Q2) + 0.369070246428542 + + """ + if not isinstance(other,Berkovich_Element_Cp): + raise ValueError("Hyperbolic metric not defined between point of Berkovich space and %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("Input to hyperbolic metric was an element of a different Berkovich space") + if self.Type() == 1 or other.Type() == 1: + if self == other: + return 0 + else: + return "oo" + return 2*((self.join(other)).diameter().log(self.prime())) - self.diameter().log(self.prime()) - other.diameter().log(other.prime()) + + def small_metric(self, other): + """ + Returns the small metric distance between ``self`` and ``other``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(1/4,4) + sage: Q2 = B(1/4,6) + sage: Q1.small_metric(Q2) + 2.00000000000000 + + """ + if not isinstance(other,Berkovich_Element_Cp): + raise ValueError("Hyperbolic metric not defined between point of Berkovich space and %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("Input to hyperbolic metric was an element of a different Berkovich space") + return 2*((self.join(other)).diameter()) - self.diameter() - other.diameter() + + def Hsia_kernel_infinity(self, other): + """ + Return the Hsia kernel at infinity evaluated at the point (``self``,``other``). + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(1/4,4) + sage: Q2 = B(1/4,6) + sage: Q1.Hsia_kernel_infinity(Q2) + 6.00000000000000 + + """ + return self.join(other).diameter() + + def center(self): + """ + Returns the center of the corresponding disk (or sequence of disks) in ``Cp`` + + OUTPUT: + + - For Type I, II, and III points, returns an element of ``Qp`` or a finite extension. For + Type IV points, returns a list of points of ``Qp`` or a finite extension + """ + if self._type == 4: + return self._center_lst[:] + return self._center + + def Type(self): + """ + Returns the Type of this point of Berkovich space over ``Cp`` + """ + return self._type + + def prime(self): + """ + Shorthand for residue characteristic of the parent + """ + return self._p + + def __ne__(self, other): + """ + Non-equality operator. + """ + return not (self == other) + + def _repr_(self): + if self._type == 1: + return "Type I point centered at " + format(self._center) + elif self._type == 2: + return "Type II point centered at " + format(self._center) + " of radius %s^%s" %(self._p,self._power) + elif self._type == 3: + return "Type III point centered at " + format(self._center) + " of radius " + format(self._radius) + else: + if self._center_func != None and self._radius_func != None: + return "Type IV point of precision %s with centers given by %s and radii given by %s" %(self._prec, self._center_func,self._radius_func) + else: + return "Type IV point of precision %s, approximated by disks centered at %s ... with radii %s ..." %(self._prec, self._center_lst[:2],self._radius_lst[:2]) + + def _latex_(self): + from sage.misc.latex import latex + if self._type == 1: + text = r"the point %s of } \Bold{C}_%s" %(self._center, self._p) + elif self._type in [2,3]: + text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" %(self._center, self._radius, self._p) + else: + text = r"the sequence of disks with centers %s } \ldots \text{ and radii %s } \ldots" %(self._center_lst[:2],self._radius_lst[:2]) + return r"\text{Type %s Point of }" %(self._type) + latex(self.parent()) + r"\text{equivalent to " + text + +class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): + r""" + Element class of the Berkovich affine line over Cp + + INPUT: + + - ``center`` -- For Type I, II, and III points, the center of the + corresponding disk in `\CC_p`. Must be an element of `\QQ_p`, a finite extension + of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers + used to approximate the point or a univariate function that computes the centers + (computation starts at 1). + - ``radius`` -- (optional) For Type I, II, and III points, the radius of the + corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, + can be a list of radii used to approximate the point or a univariate function that + computes the radii (computation starts at 1). + - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies + the power of ``p`` such that p^power = radius + - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point + - ``error_check`` -- (default: True) If error checking should be run on input. If + input is correctly formatted, can be set to ``False`` for better performance. + WARNING: with error check set to ``False``, any error in the input will lead to + incorrect results. + + EXAMPLES: + + Type I points can be created by specifying the corresponding point of ``Cp``:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: a = B(4) + sage: a + Type I point centered at 1 + 3 + O(3^20) + + The center of a point can be an element of a finite extension of ``Qp``:: + + sage: A. = Qq(27) + sage: a = B(1+t) + sage: a + Type I point centered at (t + 1) + O(3^20) + + Type II and III points can be created by specifying a center and a radius:: + + sage: b = B(2, 3**(1/2)); b + Type II point centered at 2 + O(3^20) of radius 3^1/2 + sage: c = B(2,1.6); c + Type III point centered at 2 + O(3^20) of radius 1.60000000000000 + + Some Type II points may be mistaken for Type III points:: + + sage: b = B(3, 3**0.5); b + Type III point centered at 3 + O(3^21) of radius 1.73205080756888 + + To avoid these errors, specify the power instead of the radius:: + + sage: b = B(3, power=RR(1/100000)); b + Type II point centered at 3 + O(3^21) of radius 3^1/100000 + + Type IV points can be constructed in a number of ways, the first being + from a list of centers and radii used to approximate the point:: + + sage: d = B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]); d + Type IV point of precision 3, approximated by disks centered at + [2 + O(3^20), 2 + O(3^20)] ... with radii [1.76100000000000, 1.12300000000000] ... + + Type IV points can be constructed from univariate functions, with arbitrary precision:: + + sage: A. = Qq(27) + sage: R. = PolynomialRing(A) + sage: f = (1+t)^2*x + sage: S. = PolynomialRing(RR) + sage: S = FractionField(S) + sage: g = (y+1)/y + sage: d = B(f,g,prec=100); d + Type IV point of precision 100 with centers given by + ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y + + For increased performance, error_check can be set to ``False``. WARNING: with error check set + to ``False``, any error in the input will lead to incorrect results:: + + sage: d = B(f,g,prec=100,error_check=False); d + Type IV point of precision 100 with centers given by + ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y + + TESTS:: + + sage: A= Berkovich_Cp_Affine(Qp(3)) + sage: Q1=A(3,1); Q1 + Type II point centered at 3 + O(3^21) of radius 3^0 + + sage: Q2=A(2.5,1); Q2 + Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20) of radius 3^0 + + sage: Q5=A(3,0); Q5 + Type I point centered at 3 + O(3^21) + + sage: Q1 == Q2 + True + + sage: Q1 == Q5 + False + + sage: Q3 = A(Qp(3)(3),power=0,error_check=False); Q3 + Type II point centered at 3 + O(3^21) of radius 3^0 + + sage: Q4 = A(3,3**0); Q4 + Type II point centered at 3 + O(3^21) of radius 3^0 + + sage: Q5 = A(3, power = 1/2); Q5 + Type II point centered at 3 + O(3^21) of radius 3^1/2 + + sage: Q6 = A(3, RR(3**(1/2))); Q6 + Type III point centered at 3 + O(3^21) of radius 1.73205080756888 + + sage: Q5 == Q6 + True + + sage: k = Qp(5) + sage: R. = k[] + sage: l. = k.extension(x^2-5) + sage: B=Berkovich_Cp_Affine(Qp(5)) + sage: B(w,power=1) + Type II point centered at w + O(w^41) of radius 5^1 + + """ + def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): + if not error_check and not(isinstance(center, Berkovich_Element_Cp_Projective)): + self._p = parent.prime() + self._base_space = parent.base() + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) + return + if not isinstance(parent, Berkovich_Cp_Affine): + raise ValueError("The parent of a point of Berkovich space must be Berkovich space") + self._p = parent.prime() + self._base_space = parent.base() + if isinstance(center, Berkovich_Element_Cp_Projective): + if (center.prime() == self._p): + Element.__init__(self, parent) + try: + center.center().dehomogenize(1) + except: + raise ValueError("The point at infinity of Berkovich Projective space does not convert to Berkovich Affine space") + self._center = center.center()[0] + self._radius = center._radius + self._power = center._power + self._prec = center._prec + self._type = center._type + if self._type == 4: + self._center_func = center._center_func + self._radius_func = center._center_func + self._radius_lst = center._radius_lst + center_lst = [] + for i in center._center_lst: + center_lst.append(i[0]) + self._center_lst = center_lst + return + else: + raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a point of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(),parent.base().prime())) + from sage.rings.function_field.element import is_FunctionFieldElement + from sage.rings.polynomial.polynomial_element import is_Polynomial + from sage.rings.fraction_field_element import FractionFieldElement_1poly_field + if isinstance(center, list): + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) + return + elif hasattr(center, "parent"): + if is_FunctionFieldElement(center) or is_Polynomial(center) or isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) + return + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + except: + raise ValueError("The center of a point of Affine Berkovich space over %s must convert to %s" %(self._base_space,self._base_space)) + if (center.parent()).prime() != self._p: + raise ValueError("The center of a point of Berkovich space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) + + def __eq__(self, other): + """ + Equality operator. + """ + if other is self: + return True + if not isinstance(other, Berkovich_Element_Cp_Affine): + return False + if other.parent() != self.parent(): + return False + stype = self.Type() + otype = other.Type() + if stype == otype and stype == 1: + return self.center() == other.center() + elif stype == otype and stype == 4: + raise NotImplementedError("Equality for Type IV points not yet implemented") + elif stype in [2,3] and otype in [2,3]: + if self.radius() != other.radius(): + return False + center_dist = (self.center() - other.center()).abs() + return center_dist <= self.radius() + else: + return False + + def partial_order(self,other): + """ + The standard partial order on Berkovich space + + INPUT: + - ``other`` - another point of the same Berkovich space + + OUTPUT: + - ``True`` - If self > other in the standard partial order + - ``False`` - If self < other in the standard partial order + - ``None`` - If the two points are not comparable + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2,4) + sage: Q2 = B(2,6) + sage: Q1.partial_order(Q2) + False + + sage: Q3 = B(1/2) + sage: Q1.partial_order(Q3) + True + + sage: Q4 = B(1/81,1) + sage: print(Q4.partial_order(Q1)) + None + + """ + if not isinstance(other,Berkovich_Element_Cp_Affine): + raise ValueError("partial order takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("partial order takes another point of the same Berkovich Affine line") + if self.Type() in [1,4]: + if other.Type() in [1,4]: + if self == other: + return True + else: + return None + else: + return False + else: + if other.Type() in [1,4]: + return True + else: + dist = (self.center()-other.center()).abs() + if(dist <= self.radius()): + if(other.radius() <= self.radius()): + return True + else: + return False + else: + if(dist <= other.radius()): + return False + else: + return None + + def join(self, other, basepoint="oo"): + """ + Computes the join of ``self`` and ``other``, with respect + to ``basepoint``. The join is first point that lies on the interesection + of the path from self to basepoint and the path from other to + basepoint. + + INPUT: + + - ``other`` -- the second point with which to take the join + - ``basepoint`` -- (default: Infinity) the basepoint with which + to take the join with respect to + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2,1) + sage: Q2 = B(2,2) + sage: Q1.join(Q2) + Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + + sage: Q3 = B(5) + sage: Q3.join(Q1) + Type II point centered at 2 + 3 + O(3^20) of radius 3^0 + + sage: Q3.join(Q1, basepoint=Q2) + Type II point centered at 2 + O(3^20) of radius 3^0 + + TESTS:: + + sage: Q4 = B(1/3**8+2,1) + sage: Q2.join(Q4,basepoint = Q1) + Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + + sage: Q5 = B(2,1/9) + sage: Q6 = B(1,1/27) + sage: Q4.join(Q5,basepoint=Q6) + Type II point centered at 1 + O(3^20) of radius 3^0 + + sage: Q7 = B(1/27,1/27) + sage: Q1.join(Q7,Q2) + Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + """ + if not isinstance(other,Berkovich_Element_Cp_Affine): + raise ValueError("join of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("join takes two points in the same Berkovich Affine line") + if self.Type() == 4 or other.Type() == 4: + raise NotImplementedError("join with Type IV points not implemented") + if basepoint == "oo": + dist = (self.center() - other.center()).abs() + return self.parent()(self.center(),max(dist,self.radius(),other.radius())) + else: + if not isinstance(basepoint, Berkovich_Element_Cp_Affine): + raise ValueError("basepoint for join must be a point of the Berkovich Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + if basepoint.Type() == 4: + raise NotImplementedError("join not implemented for Type IV basepoint") + b_greater_than_s = basepoint.partial_order(self) + b_greater_than_o = basepoint.partial_order(other) + if b_greater_than_s == None and b_greater_than_o == None: + dist_b_s = (self.center() - basepoint.center()).abs() + dist_b_o = (other.center() - basepoint.center()).abs() + return self.parent()(basepoint.center(),min(max(dist_b_o,other.radius(),basepoint.radius()),max(dist_b_s,self.radius(),basepoint.radius()))) + elif b_greater_than_s != None and b_greater_than_o == None: + dist_b_o = (other.center() - basepoint.center()).abs() + if dist_b_o > basepoint.radius() and dist_b_o < self.radius(): #TODO remove the check on basepoint.radius() + return (self.parent())(basepoint.center(),dist_b_o) + elif dist_b_o > basepoint.radius() and dist_b_o >= self.radius(): + if b_greater_than_s: + return basepoint + else: + return self + else: + raise NotImplementedError("impossible case in join") #TODO remove this + elif b_greater_than_s == None and b_greater_than_o != None: + return other.join(self,basepoint) + else: + if b_greater_than_s: + if b_greater_than_o: + if self.partial_order(other): + return self + else: + return other + else: + return basepoint + else: + if b_greater_than_o: + return basepoint + else: + if self.partial_order(other): + return other + else: + return self + + def potential_kernel(self, other, basepoint): + """ + The potential kernel of ``self`` with ``other``, + with basepoint ``basepoint``. + """ + if not isinstance(other,Berkovich_Element_Cp_Affine): + raise ValueError("potential kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("potential kernel takes two points in the same Berkovich Affine line") + if not isinstance(basepoint, Berkovich_Element_Cp_Affine): + raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + return basepoint.hyperbolic_metric((self.join(other,basepoint))) + + def spherical_kernel(self,other): + """ + The spherical kernel of ``self`` with ``other``. + + Equivalently, the Hsia kernel with basepoint the Gauss + point. + """ + if not isinstance(other,Berkovich_Element_Cp_Affine): + raise ValueError("spherical kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("spherical kernel takes two points in the same Berkovich Affine line") + gauss_point = (self.parent())(0,1) + w = self.join(other,gauss_point) + dist = gauss_point.hyperbolic_metric(w) + if dist == "oo": + return 0 + return (self.prime())**(-1*dist) + + def Hsia_kernel(self, other, basepoint): + """ + The Hsia kernel of ``self`` and ``other``, + with basepoint ``basepoint`` + """ + if not isinstance(other,Berkovich_Element_Cp_Affine): + raise ValueError("Hsia kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("Hsia kernel takes two points in the same Berkovich Affine line") + if not isinstance(basepoint, Berkovich_Element_Cp_Affine): + raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + if basepoint.Type() == 1: + if self == basepoint or other == basepoint: + return "oo" + return (self.spherical_kernel(other))/(self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + + def diameter(self, basepoint="oo"): + """ + Generalized diameter function. + + If the basepoint is infinity, the diameter is equal to + the limit of the radii of the corresponding disks in ``Cp``. + + If the basepoint is not infinity, the diameter + is the Hsia kernel of ``self`` with itself at + basepoint ``basepoint``. + """ + if basepoint == "oo": + return super().diameter() + else: + return self.Hsia_kernel(self,basepoint) + +class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): + r""" + Element class of the Berkovich projective line over Cp + + INPUT: + + - ``center`` -- For Type I, II, and III points, the center of the + corresponding disk in `P^1(\CC_p)`. Must be an element of `\QQ_p`, a finite extension + of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers + used to approximate the point or a univariate function that computes the centers + (computation starts at 1). + - ``radius`` -- (optional) For Type I, II, and III points, the radius of the + corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, + can be a list of radii used to approximate the point or a univariate function that + computes the radii (computation starts at 1). + - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies + the power of ``p`` such that p^power = radius + - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point + - ``error_check`` -- (default: True) If error checking should be run on input. If + input is correctly formatted, can be set to ``False`` for better performance. + + EXAMPLES:: + + Type I points can be created by specifying the corresponding point of `P^1(\CC_p)`:: + + sage: S = ProjectiveSpace(Qp(5),1) + sage: P = Berkovich_Cp_Projective(S); P + Projective Berkovich line over Cp(5) + + sage: a = S((0,1)) + sage: Q1 = P(a); Q1 + Type I point centered at (0 : 1 + O(5^20)) + + sage: Q2 = P((1,0)); Q2 + Type I point centered at (1 + O(5^20) : 0) + + Type II and III points can be created by specifying a center and a radius:: + + sage: Q3 = P((0,5),5**(3/2)); Q3 + Type II point centered at (0 : 1 + O(5^20)) of radius 5^3/2 + + sage: Q4 = P(0,3**(3/2)); Q4 + Type III point centered at (0 : 1 + O(5^20)) of radius 5.19615242270663 + + Type IV points can be created from lists of centers and radii:: + + sage: b = S((3,2)) #create centers + sage: c = S((4,3)) + sage: d = S((2,3)) + sage: L = [b,c,d] + sage: R = [1.761, 1.123, 1.112] + sage: Q5 = P(L,R); Q5 + Type IV point of precision 3, approximated by disks centered at + [(4 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + + 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20) : + 1 + O(5^20)), (3 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + + 5^10 + 3*5^11 + 5^12 + 3*5^13 + 5^14 + 3*5^15 + 5^16 + 3*5^17 + 5^18 + 3*5^19 + O(5^20) : + 1 + O(5^20))] ... with radii [1.76100000000000, 1.12300000000000] ... + + Type IV points can also be created from univariate functions. Since the centers of + the sequence of disks can not be the point at infinity in `P^1(\CC_p)`, only functions + into `\CC_p` are supported:: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q6 = P(f,g); Q6 + Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) + and radii given by 40.0000000000000*pi/x + + TEST:: + + sage: P((1,0),3) + Traceback (most recent call last): + ... + ValueError: Type II and III points cannot be centered at (1 : 0) + + """ + def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): + if not error_check and not(isinstance(center, Berkovich_Element_Cp_Affine)): + self._p = parent.prime() + self._base_space = parent.base() + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) + return + if not isinstance(parent, Berkovich_Cp_Projective): + raise ValueError("The parent of a point of Berkovich space must be Berkovich space") + self._p = parent.prime() + self._base_space = parent.base() + from sage.rings.function_field.element import is_FunctionFieldElement + from sage.rings.polynomial.polynomial_element import is_Polynomial + from sage.rings.fraction_field_element import FractionFieldElement_1poly_field + if isinstance(center, Berkovich_Element_Cp_Affine): + if (center.prime() == self._p): + Element.__init__(self, parent) + self._center = (self._base_space)(center._center) + self._radius = center._radius + self._power = center._power + self._prec = center._prec + self._type = center._type + if self._type == 4: + self._center_func = center._center_func + self._radius_func = center._center_func + self._radius_lst = center._radius_lst + center_lst = [] + for i in center._center_lst: + center_lst.append((self._base_space)(i)) + self._center_lst = center_lst + return + else: + raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a point of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(),parent.base().prime())) + if isinstance(center, list): + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) + return + elif hasattr(center, "parent"): + if is_FunctionFieldElement(center) or is_Polynomial(center) or isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) + return + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + except: + raise ValueError("The center of a point of Projective Berkovich space must be a point of P^1(Cp) or coerce into P^1(Qp)") #TODO change to Qpbar + if not(center.scheme().ambient_space() is center.scheme()): + raise ValueError("The center of a point of Projective Berkovich space cannot be a point of %s" %(center.scheme())) + if (center.scheme()).base_ring().prime() != self._p: + raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) + if (radius == None and power == None) or radius == 0: + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) + return + try: + center.dehomogenize(1) + except ValueError: + raise ValueError("Type II and III points cannot be centered at (1 : 0)") + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) + + def _field_center(self): + """ + Returns the center of ``self`` as an element + of a field, allowing for arithmetic. + Used to make handling Type II and III points + easier, as the centers of these points are never (1 : 0). + """ + return self.center()[0] + + def __eq__(self, other): + """ + Equality operator. + """ + if other is self: + return True + if not isinstance(other, Berkovich_Element_Cp_Projective): + return False + if other.parent() != self.parent(): + return False + stype = self.Type() + otype = other.Type() + if stype == otype and stype == 1: + return self.center() == other.center() + elif stype == otype and stype == 4: + raise NotImplementedError("Equality for Type IV points not yet implemented") + elif stype in [2,3] and otype in [2,3]: + if self.radius() != other.radius(): + return False + scent = self._field_center() + ocent = other._field_center() + center_dist = (scent - ocent).abs() + return center_dist <= self.radius() + else: + return False + + def partial_order(self,other): + """ + The standard partial order on Berkovich space + + INPUT: + - ``other`` - another point of the same Berkovich space + + OUTPUT: + - ``True`` - If self > other in the standard partial order + - ``False`` - If other > self in the standard partial order + - ``None`` - If the two points are not comparable + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3),1)) + sage: Q1 = B(2,4) + sage: Q2 = B(2,6) + sage: Q1.partial_order(Q2) + False + + sage: Q3 = B(1/2) + sage: Q1.partial_order(Q3) + True + + sage: Q4 = B(1/81,1) + sage: print(Q4.partial_order(Q1)) + None + + """ + if not isinstance(other,Berkovich_Element_Cp_Projective): + raise ValueError("partial order takes another point of the Berkovich Projective line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("partial order takes another point of the same Berkovich Projective line") + if self.Type() in [1,4]: + if other.Type() in [1,4]: + if self == other: + return True + else: + return None + else: + return False + else: + if other.Type() in [1,4]: + return True + else: + dist = (self._field_center()-other._field_center()).abs() + if(dist <= self.radius()): + if(other.radius() <= self.radius()): + return True + else: + return False + else: + if(dist <= other.radius()): + return False + else: + return None + + def join(self, other, basepoint="oo"): + """ + Computes the join of ``self`` and ``other``, with respect + to ``basepoint``. The join is first point that lies on the interesection + of the path from self to basepoint and the path from other to + basepoint. + + INPUT: + + - ``other`` -- the second point with which to take the join + - ``basepoint`` -- (default: Infinity) the basepoint with which + to take the join with respect to + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3),1)) + sage: Q1 = B(2,1) + sage: Q2 = B(2,2) + sage: Q1.join(Q2) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q3 = B(5) + sage: Q3.join(Q1) + Type II point centered at (2 + 3 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + sage: Q3.join(Q1, basepoint=Q2) + Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + TESTS:: + + sage: Q4 = B(1/3**8+2,1) + sage: Q2.join(Q4,basepoint = Q1) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q5 = B(2,1/9) + sage: Q6 = B(1,1/27) + sage: Q4.join(Q5,basepoint=Q6) + Type II point centered at (1 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + sage: Q7 = B(1/27,1/27) + sage: Q1.join(Q7,Q2) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + """ + if not isinstance(other,Berkovich_Element_Cp_Projective): + raise ValueError("join of a point of the Berkovich Projective line takes another point of the Berkovich Projective line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("join takes two points in the same Berkovich Projective line") + if self.Type() == 4 or other.Type() == 4: + raise NotImplementedError("join with Type IV points not implemented") + infty = (self.parent())((1,0)) + if basepoint == "oo" or basepoint == infty: + if self == infty or other == infty: + return infty + else: + dist = (self._field_center() - other._field_center()).abs() + return self.parent()(self.center(),max(dist,self.radius(),other.radius())) + else: + if not isinstance(basepoint, Berkovich_Element_Cp_Projective): + raise ValueError("basepoint for join must be a point of the Berkovich Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + if basepoint.Type() == 4: + raise NotImplementedError("join not implemented for Type IV basepoint") + if self == infty or other == infty: + if self == other: + return infty + else: + if self == infty: + return other.join(basepoint) + else: + return self.join(basepoint) + else: + base = self._base_space.base_ring() + B = Berkovich_Cp_Affine(base) + affine_self = B(self) + affine_other = B(other) + affine_basepoint = B(basepoint) + affine_join = affine_self.join(affine_other,affine_basepoint) + return (self.parent())(affine_join) + + def potential_kernel(self, other, basepoint): + """ + The potential kernel of ``self`` with ``other``, + with basepoint ``basepoint``. + """ + if not isinstance(other,Berkovich_Element_Cp_Projective): + raise ValueError("potential kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("potential kernel takes two points in the same Berkovich Affine line") + if not isinstance(basepoint, Berkovich_Element_Cp_Projective): + raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + return basepoint.hyperbolic_metric((self.join(other,basepoint))) + + def spherical_kernel(self,other): + """ + The spherical kernel of ``self`` with ``other``. + + Equivalently, the Hsia kernel with basepoint the Gauss + point. + """ + if not isinstance(other,Berkovich_Element_Cp_Projective): + raise ValueError("spherical kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("spherical kernel takes two points in the same Berkovich Affine line") + gauss_point = (self.parent())(0,1) + w = self.join(other,gauss_point) + dist = gauss_point.hyperbolic_metric(w) + if dist == "oo": + return 0 + return (self.prime())**(-1*dist) + + def Hsia_kernel(self, other, basepoint): + """ + The Hsia kernel of ``self`` and ``other``, + with basepoint ``basepoint`` + """ + if not isinstance(other,Berkovich_Element_Cp_Projective): + raise ValueError("Hsia kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("Hsia kernel takes two points in the same Berkovich Affine line") + if not isinstance(basepoint, Berkovich_Element_Cp_Projective): + raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + if basepoint.Type() == 1: + if self == basepoint or other == basepoint: + return "oo" + return (self.spherical_kernel(other))/(self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + + def diameter(self, basepoint="oo"): + """ + Generalized diameter function. + + If the basepoint is infinity, the diameter is equal to + the limit of the radii of the corresponding disks in ``Cp``. + + If the basepoint is not infinity, the diameter + is the Hsia kernel of ``self`` with itself at + basepoint ``basepoint``. + """ + if basepoint == "oo": + return super().diameter() + else: + return self.Hsia_kernel(self,basepoint) + +class Berkovich(UniqueRepresentation,Parent): + """ + The parent class for any Berkovich space + """ + pass + +class Berkovich_Cp_Affine(Berkovich): + """ + The Berkovich affine line over Cp + """ + + def __init__(self,base): + from sage.rings.padics.generic_nodes import is_pAdicField + if not is_pAdicField(base): #TODO change base to Qpbar(prime) + raise ValueError("Base of Berkovich Space must be a padic field") + self._p = base.prime() + Parent.__init__(self, base = base, category=TopologicalSpaces()) + + def residue_characteristic(self): + return self._p + + def prime(self): + return self._p + + def _coerce_map_from_(self,S): + if isinstance(S, Berkovich_Cp_Affine): + if S.prime() == self.prime(): + return True + return False + + def _repr_(self): + return "Affine Berkovich line over Cp(%s)" %(self.prime()) + + def _latex_(self): + return r"\text{Affine Berkovich line over } \Bold{C}_{%s}" %(self.prime()) + + Element = Berkovich_Element_Cp_Affine + +class Berkovich_Cp_Projective(Berkovich): + """ + The Berkovich projective line over Cp + """ + + def __init__(self,base): + from sage.schemes.projective.projective_space import is_ProjectiveSpace + if not is_ProjectiveSpace(base): + raise ValueError("Base of Projective Berkovich Space must be Projective Space") + from sage.rings.padics.generic_nodes import is_pAdicField + if not (is_pAdicField(base.base_ring())): + raise ValueError("Base of Projective Berkovich Space must be Projective Space over Qp") + self._p = base.base_ring().prime() + Parent.__init__(self, base = base, category=TopologicalSpaces()) + + def residue_characteristic(self): + return self._p + + def prime(self): + return self._p + + def _coerce_map_from_(self,S): + if isinstance(S, Berkovich_Cp_Affine) or isinstance(S,Berkovich_Cp_Projective): + if S.prime() == self.prime(): + return True + return False + + def _repr_(self): + return "Projective Berkovich line over Cp(%s)" %(self.prime()) + + def _latex_(self): + return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) + + Element = Berkovich_Element_Cp_Projective + + From a6ccdeee28d97247e18cb9d0da75f980f9af1084 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 15 Jun 2020 21:47:38 -0400 Subject: [PATCH 005/379] 29844: Cleaned up and commented element init, added more documentation --- src/sage/schemes/berkovich/berkovich_space.py | 427 ++++++++++++------ 1 file changed, 291 insertions(+), 136 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 6aae9489274..f165a9dd936 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -16,7 +16,7 @@ from sage.schemes.affine.affine_space import AffineSpace from sage.schemes.generic.scheme import Scheme from sage.rings.rational_field import QQ -from sage.rings.real_mpfr import is_RealNumber + class Berkovich_Element(Element): @@ -28,8 +28,8 @@ class Berkovich_Element(Element): class Berkovich_Element_Cp(Berkovich_Element): """ The parent class for any element of Berkovich space over ``Cp``. - This class should never be instantiated, instead BerkSpaceCpAffineElement - or BerkSpaceCpProjective should be used. + This class should never be instantiated, instead Berkovich_Element_Cp_Affine + or Berkovich_Element_Cp_Projective should be used. """ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, error_check=True): @@ -38,12 +38,15 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.fraction_field_element import FractionFieldElement_1poly_field self._type = None + + #if center is a list, this is a Type 4 point if isinstance(center, list): if error_check: if not isinstance(radius, list): raise ValueError("center was passed a list but radius was not a list") if len(radius) != len(center): - raise ValueError("The same number of centers and radii must be specified to create a Type IV point") + raise ValueError("The same number of centers and radii must be specified to create \ + a Type IV point") self._center_lst = list(center) self._radius_lst = list(radius) self._prec = len(self._radius_lst) @@ -52,22 +55,30 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._type = 4 if not error_check: return + + #is_FunctionFieldElement calls .parent elif hasattr(center, "parent"): - if is_FunctionFieldElement(center) or is_Polynomial(center) or isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): + #if center is a supported univariate function, this is a Type 4 point + if is_FunctionFieldElement(center) or is_Polynomial(center) or\ + isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): #check that both center and radii are supported univariate function center_expr_check = False radius_expr_check = False if error_check: if is_Expression(center): if len(center.variables()) != 1: - raise ValueError("An expression with %s variables cannot define the centers approximating a Type IV point" %(len(center.variables()))) + raise ValueError("An expression with %s variables cannot define \ + the centers approximating a Type IV point" %(len(center.variables()))) else: + #we do this since .subs is currently bugged for polynomials but not expressions center_expr_check = True - if not (is_FunctionFieldElement(radius) or is_Polynomial(radius) or isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius)): + if not (is_FunctionFieldElement(radius) or is_Polynomial(radius) or\ + isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius)): raise ValueError("center was passed a function but radius was not a function") if is_Expression(radius): if len(radius.variables()) != 1: - raise ValueError("An expression with %s variables cannot define the radii approximating a Type IV point" %(len(radius.variables()))) + raise ValueError("An expression with %s variables cannot define the radii \ + approximating a Type IV point" %(len(radius.variables()))) else: radius_expr_check = True else: @@ -87,9 +98,11 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, y = self._radius_func.variables()[0] for i in range(1,self._prec+1): if center_expr_check: - center_lst.append(self._center_func.subs({x:i})) #we use .subs for expressions to avoid deprecation + #we use .subs for expressions to avoid deprecation + center_lst.append(self._center_func.subs({x:i})) else: - center_lst.append(self._center_func(i)) #.subs for polynomials is currently bugged + #.subs for polynomials is currently bugged + center_lst.append(self._center_func(i)) if radius_expr_check: radius_lst.append(self._radius_func.subs({y:i})) else: @@ -98,21 +111,31 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._radius_lst = radius_lst if not error_check: return + if self._type == 4 and error_check: + from sage.rings.real_mpfr import is_RealNumber if child == "projective": for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] + #make sure the center is a point of projective space and not the point at infinity if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) self._center_lst[i] = center except: - raise ValueError("The center of a point of Projective Berkovich space must be a point of P^1(Cp(%s))"%(self._p)) + raise ValueError("The center of a point of Projective Berkovich \ + space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) if center.scheme().ambient_space() != center.scheme(): - raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) + raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) \ + cannot be a point of %s" %(self._p,center.scheme())) if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) + raise ValueError("The center of a disk approximating a Type IV point of Berkovich space\ + over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) + if center == (center.scheme())((1,0)): + raise ValueError("The center of a disk approximating a Type IV point of Berkovich \ + space cannot be centered at %s" %((center.scheme())((1,0)))) + #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): radius = RR(radius) @@ -120,26 +143,33 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("The radius of a disk approximating a Type IV point must coerce into the real numbers, %s does not coerce" %(radius)) + raise ValueError("The radius of a disk approximating a Type IV point \ + must coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: + #check containment for the sequence of disks previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] dist = (center[0] - previous_center[0]).abs() if previous_radius < radius or dist > radius: - raise ValueError("Sequence of disks does not define a Type IV point as containment is not proper") + raise ValueError("Sequence of disks does not define a Type IV point as \ + containment is not proper") return elif child == "affine": for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] + #make sure the center is in Cp if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) self._center_lst[i] = center except: - raise ValueError("The center of a disk approximating a Type IV point must be padic or coerce into Qp, %s does not coerse into Qp" %(center)) + raise ValueError("The center of a disk approximating a Type IV point must \ + be padic or coerce into Qp, %s does not coerse into Qp" %(center)) if (center.parent()).prime() != self._p: - raise ValueError("The center of a disk approximating a Type IV point of Berkovich space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + raise ValueError("The center of a disk approximating a Type IV point of Berkovich \ + space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): radius = RR(radius) @@ -147,24 +177,61 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("The radius of a disk approximating a Type IV point must coerce into the real numbers, %s does not coerce" %(radius)) + raise ValueError("The radius of a disk approximating a Type IV point must \ + coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: + #check containment for the sequence of disks previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] dist = (center - previous_center).abs() if previous_radius < radius or dist > radius: - raise ValueError("Sequence of disks does not define a Type IV point as containment is not proper") + raise ValueError("Sequence of disks does not define a Type IV point as \ + containment is not proper") return else: - raise ValueError("bad value %s passed to child. Do not initialize BerkSpaceCpElement directly" %(child)) + raise ValueError("bad value %s passed to child. Do not initialize Berkovich_Element_Cp \ + directly" %(child)) return - self._prec = None + + #the point must now be Type 1, 2, or 3, so we check that the center is of the appropriate type + if child == "affine" and error_check: + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + except: + raise ValueError("The center of a point of Affine Berkovich space over %s must convert \ + to %s" %(self._base_space,self._base_space)) + if (center.parent()).prime() != self._p: + raise ValueError("The center of a point of Berkovich space over Cp(%s) cannot be a point of %s")\ + %(self._p, center.parent()) + elif child == "projective" and error_check: + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + except: + raise ValueError("The center of a point of Projective Berkovich space must be a \ + point of P^1(Cp) or coerce into P^1(Qp)") #TODO change to Qpbar + if not(center.scheme().ambient_space() is center.scheme()): + raise ValueError("The center of a point of Projective Berkovich space cannot be \ + a point of %s" %(center.scheme())) + if (center.scheme()).base_ring().prime() != self._p: + raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot \ + be a point of %s") %(self._p, center.scheme()) + elif child != "affine" and child != "projective": + raise ValueError("bad value %s passed to child. Do not initialize Berkovich_Element_Cp \ + directly" %(child)) self._center = center if (radius == None and power == None) or radius == 0: self._type = 1 self._radius = 0 self._power = None return + #In order to simplify our representation, Type II and III points cannot be centered at infinity + if child == "projective": + try: + center.dehomogenize(1) + except ValueError: + raise ValueError("Type II and III points cannot be centered at (1 : 0)") if power != None: if error_check: if power.parent() != QQ: @@ -190,6 +257,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._radius = radius except: raise ValueError("Symbolic radius must be a real number") + from sage.rings.real_mpfr import is_RealNumber if (not is_RealNumber(radius)) and power == None: if RR.has_coerce_map_from(radius.parent()): self._radius = RR(radius) @@ -224,7 +292,14 @@ def radius(self): def diameter(self): """ - Diameter function on Berkovich space + Diameter function on Berkovich space. + + For Type I, II, and III points, returns the radius. + For Type IV points returns either the last radius + in the finite approximation, or if a generating function + was given for the radii, the diameter is computed + as the limit of the function as it's variable tends + to infinity. EXAMPLES:: @@ -257,7 +332,6 @@ def diameter(self): def hyperbolic_metric(self, other): """ Returns the hypebolic distance metric distance between ``self`` and ``other``. - Not defined for Type I points. EXAMPLES:: @@ -272,7 +346,7 @@ def hyperbolic_metric(self, other): raise ValueError("Hyperbolic metric not defined between point of Berkovich space and %s" %(other)) if self.parent() != other.parent(): raise ValueError("Input to hyperbolic metric was an element of a different Berkovich space") - if self.Type() == 1 or other.Type() == 1: + if self.type_of_point() == 1 or other.type_of_point() == 1: if self == other: return 0 else: @@ -326,7 +400,7 @@ def center(self): return self._center_lst[:] return self._center - def Type(self): + def type_of_point(self): """ Returns the Type of this point of Berkovich space over ``Cp`` """ @@ -369,7 +443,18 @@ def _latex_(self): class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" - Element class of the Berkovich affine line over Cp + Element class of the Berkovich affine line over `\CC_p` + + Elements are categorized into four Types, represented by specific data: + + - Type I points are represented by a center in `\QQ_p` or a finite extension + + - Type II points are represented by a center in `\QQ_p` and a rational power of `p` + + - Type III points are represented by a center in `\QQ_p` and a radius in `[0,\infty)` + + - Type IV points are represented by a finite list of centers in `\QQ_p` and + a finite list of radii in `[0,\infty)`. INPUT: @@ -378,13 +463,17 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers used to approximate the point or a univariate function that computes the centers (computation starts at 1). + - ``radius`` -- (optional) For Type I, II, and III points, the radius of the corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, can be a list of radii used to approximate the point or a univariate function that computes the radii (computation starts at 1). + - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies the power of ``p`` such that p^power = radius + - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point + - ``error_check`` -- (default: True) If error checking should be run on input. If input is correctly formatted, can be set to ``False`` for better performance. WARNING: with error check set to ``False``, any error in the input will lead to @@ -492,28 +581,26 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): - if not error_check and not(isinstance(center, Berkovich_Element_Cp_Projective)): - self._p = parent.prime() - self._base_space = parent.base() - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) - return - if not isinstance(parent, Berkovich_Cp_Affine): - raise ValueError("The parent of a point of Berkovich space must be Berkovich space") + #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space + #unless we are passed a point of projective Berkovich space self._p = parent.prime() self._base_space = parent.base() + + #if this is a point of projective berkovich space, we do the conversion if isinstance(center, Berkovich_Element_Cp_Projective): if (center.prime() == self._p): Element.__init__(self, parent) try: center.center().dehomogenize(1) except: - raise ValueError("The point at infinity of Berkovich Projective space does not convert to Berkovich Affine space") + raise ValueError("The point at infinity of Berkovich Projective space does not \ + convert to Berkovich Affine space") self._center = center.center()[0] self._radius = center._radius self._power = center._power - self._prec = center._prec self._type = center._type if self._type == 4: + self._prec = center._prec self._center_func = center._center_func self._radius_func = center._center_func self._radius_lst = center._radius_lst @@ -523,25 +610,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a point of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(),parent.base().prime())) - from sage.rings.function_field.element import is_FunctionFieldElement - from sage.rings.polynomial.polynomial_element import is_Polynomial - from sage.rings.fraction_field_element import FractionFieldElement_1poly_field - if isinstance(center, list): - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) - return - elif hasattr(center, "parent"): - if is_FunctionFieldElement(center) or is_Polynomial(center) or isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) - return - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - except: - raise ValueError("The center of a point of Affine Berkovich space over %s must convert to %s" %(self._base_space,self._base_space)) - if (center.parent()).prime() != self._p: - raise ValueError("The center of a point of Berkovich space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="affine",error_check=error_check) + raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a \ + point of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(), \ + parent.base().prime())) + + #otherwise we call the Berkovich_Element_Cp constructor with child=affine + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ + prec=prec,child="affine",error_check=error_check) def __eq__(self, other): """ @@ -553,8 +628,8 @@ def __eq__(self, other): return False if other.parent() != self.parent(): return False - stype = self.Type() - otype = other.Type() + stype = self.type_of_point() + otype = other.type_of_point() if stype == otype and stype == 1: return self.center() == other.center() elif stype == otype and stype == 4: @@ -600,8 +675,8 @@ def partial_order(self,other): raise ValueError("partial order takes another point of the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("partial order takes another point of the same Berkovich Affine line") - if self.Type() in [1,4]: - if other.Type() in [1,4]: + if self.type_of_point() in [1,4]: + if other.type_of_point() in [1,4]: if self == other: return True else: @@ -609,7 +684,7 @@ def partial_order(self,other): else: return False else: - if other.Type() in [1,4]: + if other.type_of_point() in [1,4]: return True else: dist = (self.center()-other.center()).abs() @@ -624,19 +699,20 @@ def partial_order(self,other): else: return None - def join(self, other, basepoint="oo"): + def join(self, other, basepoint="infty"): """ - Computes the join of ``self`` and ``other``, with respect - to ``basepoint``. The join is first point that lies on the interesection + Computes the join of ``self`` and ``other`` with respect to ``basepoint``. + + The join is first point that lies on the interesection of the path from self to basepoint and the path from other to basepoint. INPUT: - + - ``other`` -- the second point with which to take the join - ``basepoint`` -- (default: Infinity) the basepoint with which to take the join with respect to - + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -671,9 +747,9 @@ def join(self, other, basepoint="oo"): raise ValueError("join of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("join takes two points in the same Berkovich Affine line") - if self.Type() == 4 or other.Type() == 4: + if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with Type IV points not implemented") - if basepoint == "oo": + if basepoint == "infty": dist = (self.center() - other.center()).abs() return self.parent()(self.center(),max(dist,self.radius(),other.radius())) else: @@ -681,7 +757,7 @@ def join(self, other, basepoint="oo"): raise ValueError("basepoint for join must be a point of the Berkovich Affine line over Cp") if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") - if basepoint.Type() == 4: + if basepoint.type_of_point() == 4: raise NotImplementedError("join not implemented for Type IV basepoint") b_greater_than_s = basepoint.partial_order(self) b_greater_than_o = basepoint.partial_order(other) @@ -722,8 +798,7 @@ def join(self, other, basepoint="oo"): def potential_kernel(self, other, basepoint): """ - The potential kernel of ``self`` with ``other``, - with basepoint ``basepoint``. + The potential kernel of ``self`` with ``other``, with basepoint ``basepoint``. """ if not isinstance(other,Berkovich_Element_Cp_Affine): raise ValueError("potential kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) @@ -755,8 +830,7 @@ def spherical_kernel(self,other): def Hsia_kernel(self, other, basepoint): """ - The Hsia kernel of ``self`` and ``other``, - with basepoint ``basepoint`` + The Hsia kernel of ``self`` and ``other``, with basepoint ``basepoint``. """ if not isinstance(other,Berkovich_Element_Cp_Affine): raise ValueError("Hsia kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) @@ -766,7 +840,7 @@ def Hsia_kernel(self, other, basepoint): raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") - if basepoint.Type() == 1: + if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return "oo" return (self.spherical_kernel(other))/(self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) @@ -789,7 +863,24 @@ def diameter(self, basepoint="oo"): class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" - Element class of the Berkovich projective line over Cp + Element class of the Berkovich Projective line over `\CC_p` + + Elements are categorized into four Types, which are represented as follows: + + - Type I points are represented by a center in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` + + - Type II points are represented by a center in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` and a rational power of `p`. + Type II points cannot be centered at the point at infinity. + + - Type III points are represented by a center in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` and a radius in [0,`\infty`). + Type III points are cannot be centered at the point at infinity. + + - Type IV points are represented by finite list of centers in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` and by a finite list of radii in [0,`\infty`). + None of the centers can be the point at infinity. INPUT: @@ -798,15 +889,20 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers used to approximate the point or a univariate function that computes the centers (computation starts at 1). + - ``radius`` -- (optional) For Type I, II, and III points, the radius of the corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, can be a list of radii used to approximate the point or a univariate function that computes the radii (computation starts at 1). + - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies the power of ``p`` such that p^power = radius + - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point + - ``error_check`` -- (default: True) If error checking should be run on input. If input is correctly formatted, can be set to ``False`` for better performance. + WARNING: Setting error_check to ``False`` can lead to incorrect results. EXAMPLES:: @@ -814,7 +910,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: S = ProjectiveSpace(Qp(5),1) sage: P = Berkovich_Cp_Projective(S); P - Projective Berkovich line over Cp(5) + Projective Berkovich line over Cp(5) of precision 20 sage: a = S((0,1)) sage: Q1 = P(a); Q1 @@ -869,27 +965,22 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): - if not error_check and not(isinstance(center, Berkovich_Element_Cp_Affine)): - self._p = parent.prime() - self._base_space = parent.base() - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) - return - if not isinstance(parent, Berkovich_Cp_Projective): - raise ValueError("The parent of a point of Berkovich space must be Berkovich space") + #if we are given a point of Affine Berkovich Space, we do the conversion + #otherwise we call the Berkovich_Element_Cp constructor with child="projective" + self._p = parent.prime() self._base_space = parent.base() - from sage.rings.function_field.element import is_FunctionFieldElement - from sage.rings.polynomial.polynomial_element import is_Polynomial - from sage.rings.fraction_field_element import FractionFieldElement_1poly_field + + #conversion from Affine points is handled in this constructor if isinstance(center, Berkovich_Element_Cp_Affine): if (center.prime() == self._p): Element.__init__(self, parent) self._center = (self._base_space)(center._center) self._radius = center._radius self._power = center._power - self._prec = center._prec self._type = center._type if self._type == 4: + self._prec = center._prec self._center_func = center._center_func self._radius_func = center._center_func self._radius_lst = center._radius_lst @@ -899,31 +990,11 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a point of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(),parent.base().prime())) - if isinstance(center, list): - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) - return - elif hasattr(center, "parent"): - if is_FunctionFieldElement(center) or is_Polynomial(center) or isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) - return - if not isinstance(center, SchemeMorphism_point_projective_field): - try: - center = (self._base_space)(center) - except: - raise ValueError("The center of a point of Projective Berkovich space must be a point of P^1(Cp) or coerce into P^1(Qp)") #TODO change to Qpbar - if not(center.scheme().ambient_space() is center.scheme()): - raise ValueError("The center of a point of Projective Berkovich space cannot be a point of %s" %(center.scheme())) - if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) - if (radius == None and power == None) or radius == 0: - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) - return - try: - center.dehomogenize(1) - except ValueError: - raise ValueError("Type II and III points cannot be centered at (1 : 0)") - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,prec=prec,child="projective",error_check=error_check) + raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a point \ + of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(),parent.base().prime())) + + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ + prec=prec,child="projective",error_check=error_check) def _field_center(self): """ @@ -944,8 +1015,8 @@ def __eq__(self, other): return False if other.parent() != self.parent(): return False - stype = self.Type() - otype = other.Type() + stype = self.type_of_point() + otype = other.type_of_point() if stype == otype and stype == 1: return self.center() == other.center() elif stype == otype and stype == 4: @@ -993,8 +1064,8 @@ def partial_order(self,other): raise ValueError("partial order takes another point of the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("partial order takes another point of the same Berkovich Projective line") - if self.Type() in [1,4]: - if other.Type() in [1,4]: + if self.type_of_point() in [1,4]: + if other.type_of_point() in [1,4]: if self == other: return True else: @@ -1002,7 +1073,7 @@ def partial_order(self,other): else: return False else: - if other.Type() in [1,4]: + if other.type_of_point() in [1,4]: return True else: dist = (self._field_center()-other._field_center()).abs() @@ -1017,10 +1088,11 @@ def partial_order(self,other): else: return None - def join(self, other, basepoint="oo"): + def join(self, other, basepoint="infty"): """ - Computes the join of ``self`` and ``other``, with respect - to ``basepoint``. The join is first point that lies on the interesection + Computes the join of ``self`` and ``other``, with respect to ``basepoint``. + + The join is first point that lies on the interesection of the path from self to basepoint and the path from other to basepoint. @@ -1064,10 +1136,10 @@ def join(self, other, basepoint="oo"): raise ValueError("join of a point of the Berkovich Projective line takes another point of the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("join takes two points in the same Berkovich Projective line") - if self.Type() == 4 or other.Type() == 4: + if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with Type IV points not implemented") infty = (self.parent())((1,0)) - if basepoint == "oo" or basepoint == infty: + if basepoint == "infty" or basepoint == infty: if self == infty or other == infty: return infty else: @@ -1078,7 +1150,7 @@ def join(self, other, basepoint="oo"): raise ValueError("basepoint for join must be a point of the Berkovich Affine line over Cp") if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") - if basepoint.Type() == 4: + if basepoint.type_of_point() == 4: raise NotImplementedError("join not implemented for Type IV basepoint") if self == infty or other == infty: if self == other: @@ -1143,7 +1215,7 @@ def Hsia_kernel(self, other, basepoint): raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") - if basepoint.Type() == 1: + if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return "oo" return (self.spherical_kernel(other))/(self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) @@ -1171,11 +1243,50 @@ class Berkovich(UniqueRepresentation,Parent): pass class Berkovich_Cp_Affine(Berkovich): - """ - The Berkovich affine line over Cp - """ + r""" + The Berkovich Affine line over `\CC_p`. + + The Berkovich Affine line can be thought of as the set of seminorms on `\CC_p[x]`, + with the weakest topology that makes the map `| \cdot | \to |f|` continuous + for all `f \in \CC_p[x]`. + + INPUT: + + - ``base`` - The prime ``p``. Alternative, can be `\QQ_p` or a finite extension. + This allows for more control over automated conversion of centers of points. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3); B + Affine Berkovich line over Cp(3) of precision 20 + + Initializing by passing in Qp space looks the same:: + + sage: B = Berkovich_Cp_Affine(Qp(3)); B + Affine Berkovich line over Cp(3) of precision 20 + + However, this method allows for more control over behind-the-scenes conversion:: + sage: B = Berkovich_Cp_Affine(Qp(3,1)); B + Affine Berkovich line over Cp(3) of precision 1 + + sage: Q1 = B(1/2); Q1 + Type I point centered at 2 + O(3) + + Note that this point has very low precision, as B was initialized + with a padic field of capped-relative precision one. For high precision, + pass in a high precision padic field:: + + sage: B = Berkovich_Cp_Affine(Qp(3,1000)); B + Affine Berkovich line over Cp(3) of precision 1000 + """ def __init__(self,base): + from sage.rings.integer_ring import ZZ + if base in ZZ: + if base.is_prime(): + base = Qp(base) #TODO chance to Qpbar + else: + raise ValueError("Non-prime pased into Berkovich space") from sage.rings.padics.generic_nodes import is_pAdicField if not is_pAdicField(base): #TODO change base to Qpbar(prime) raise ValueError("Base of Berkovich Space must be a padic field") @@ -1195,7 +1306,7 @@ def _coerce_map_from_(self,S): return False def _repr_(self): - return "Affine Berkovich line over Cp(%s)" %(self.prime()) + return "Affine Berkovich line over Cp(%s) of precision %s" %(self.prime(),self.base().precision_cap()) def _latex_(self): return r"\text{Affine Berkovich line over } \Bold{C}_{%s}" %(self.prime()) @@ -1203,17 +1314,60 @@ def _latex_(self): Element = Berkovich_Element_Cp_Affine class Berkovich_Cp_Projective(Berkovich): - """ - The Berkovich projective line over Cp - """ + r""" + The Berkovich Projective line over `\CC_p`. + + The Berkovich Projective line can be thought of as the one-point compactification + of the Berkovich Affine line. + + INPUT: + + - base - The prime number `p`. Alternatively, can be a Projective Space over + `\QQ_p` or a finite extension of `\QQ_p`. This allows for more control of conversion of centers. + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3); B + Projective Berkovich line over Cp(3) of precision 20 + + Initializing by passing in a projective space looks the same:: + + sage: S = ProjectiveSpace(Qp(3),1) + sage: B = Berkovich_Cp_Projective(S); B + Projective Berkovich line over Cp(3) of precision 20 + + However, this method allows for more control over behind-the-scenes conversion:: + + sage: S = ProjectiveSpace(Qp(3,1),1) + sage: B = Berkovich_Cp_Projective(S); B + Projective Berkovich line over Cp(3) of precision 1 + + sage: Q1 = B(1/2); Q1 + Type I point centered at (2 + O(3) : 1 + O(3)) + + Note that this point has very low precision, as S is a scheme over + Qp(3) of capped-relative precision one. + + """ def __init__(self,base): - from sage.schemes.projective.projective_space import is_ProjectiveSpace - if not is_ProjectiveSpace(base): - raise ValueError("Base of Projective Berkovich Space must be Projective Space") - from sage.rings.padics.generic_nodes import is_pAdicField - if not (is_pAdicField(base.base_ring())): - raise ValueError("Base of Projective Berkovich Space must be Projective Space over Qp") + from sage.rings.integer_ring import ZZ + if base in ZZ: + if base.is_prime(): + base = ProjectiveSpace(Qp(base),1) + else: + raise ValueError("Non-prime pased into Berkovich space") + else: + from sage.schemes.projective.projective_space import is_ProjectiveSpace + if not is_ProjectiveSpace(base): + raise ValueError("Base of Projective Berkovich Space must be Projective Space") + from sage.rings.padics.generic_nodes import is_pAdicField + if not (is_pAdicField(base.base_ring())): + raise ValueError("Base of Projective Berkovich Space must be Projective Space over Qp") + if base.ambient_space() != base: + raise ValueError("Base of Projective Berkovich Space must be Projective Space over Qp") + if base.dimension() != 1: + raise ValueError("Base of Projective Berkovich Space must be Projective Space of \ + dimension 1 over Qp") self._p = base.base_ring().prime() Parent.__init__(self, base = base, category=TopologicalSpaces()) @@ -1230,7 +1384,8 @@ def _coerce_map_from_(self,S): return False def _repr_(self): - return "Projective Berkovich line over Cp(%s)" %(self.prime()) + return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ + self.base().base_ring().precision_cap()) def _latex_(self): return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) From 2102c5d007b2c50a037ada82280b4880a02c7b56 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 16 Jun 2020 19:45:25 -0400 Subject: [PATCH 006/379] 29844: Fixed mathematically incorrect small_metric. Added more documentation + fixed small bugs --- src/sage/schemes/berkovich/berkovich_space.py | 737 ++++++++++++++---- 1 file changed, 571 insertions(+), 166 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index f165a9dd936..e8930a98bb6 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -8,6 +8,7 @@ from sage.categories.topological_spaces import TopologicalSpaces from sage.symbolic.expression import is_Expression from sage.rings.real_mpfr import RR +from sage.rings.padics.generic_nodes import pAdicFieldGeneric from sage.rings.padics.padic_generic_element import pAdicGenericElement from sage.rings.padics.factory import Qp from sage.schemes.projective.projective_space import ProjectiveSpace @@ -45,8 +46,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if not isinstance(radius, list): raise ValueError("center was passed a list but radius was not a list") if len(radius) != len(center): - raise ValueError("The same number of centers and radii must be specified to create \ - a Type IV point") + raise ValueError("The same number of centers and radii must be specified to create " + \ + "a Type IV point") self._center_lst = list(center) self._radius_lst = list(radius) self._prec = len(self._radius_lst) @@ -67,8 +68,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if error_check: if is_Expression(center): if len(center.variables()) != 1: - raise ValueError("An expression with %s variables cannot define \ - the centers approximating a Type IV point" %(len(center.variables()))) + raise ValueError("An expression with %s " %(len(center.variables())) + \ + "variables cannot define the centers approximating a Type IV point") else: #we do this since .subs is currently bugged for polynomials but not expressions center_expr_check = True @@ -77,8 +78,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("center was passed a function but radius was not a function") if is_Expression(radius): if len(radius.variables()) != 1: - raise ValueError("An expression with %s variables cannot define the radii \ - approximating a Type IV point" %(len(radius.variables()))) + raise ValueError("An expression with %s " %(len(radius.variables())) + \ + "variables cannot define the radii approximating a Type IV point") else: radius_expr_check = True else: @@ -123,18 +124,36 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, try: center = (self._base_space)(center) self._center_lst[i] = center + flag = False except: - raise ValueError("The center of a point of Projective Berkovich \ - space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) + flag = True + if flag: + raise ValueError("The center of a point of Projective Berkovich" + \ + "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) + elif not isinstance(center.scheme().base_ring(), pAdicFieldGeneric): + if not isinstance(center.scheme().base_ring(),pAdicGeneric): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("The center of a point of Projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s") %self._base_space + else: + try: + center = (self._base_space)(center) + except: + pass if center.scheme().ambient_space() != center.scheme(): - raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) \ - cannot be a point of %s" %(self._p,center.scheme())) + raise ValueError("The center of a point of Berkovich space over " + \ + "P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("The center of a disk approximating a Type IV point of Berkovich space\ - over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) + raise ValueError("The center of a disk approximating a Type IV point of Berkovich space" + \ + " over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) if center == (center.scheme())((1,0)): - raise ValueError("The center of a disk approximating a Type IV point of Berkovich \ - space cannot be centered at %s" %((center.scheme())((1,0)))) + raise ValueError("The center of a disk approximating a Type IV point of Berkovich " + \ + "space cannot be centered at %s" %((center.scheme())((1,0)))) #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): @@ -143,16 +162,16 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("The radius of a disk approximating a Type IV point \ - must coerce into the real numbers, %s does not coerce" %(radius)) + raise ValueError("The radius of a disk approximating a Type IV point" + \ + "must coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] dist = (center[0] - previous_center[0]).abs() if previous_radius < radius or dist > radius: - raise ValueError("Sequence of disks does not define a Type IV point as \ - containment is not proper") + raise ValueError("Sequence of disks does not define a Type IV point as" + \ + "containment is not proper") return elif child == "affine": for i in range(len(self._center_lst)): @@ -163,12 +182,20 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, try: center = (self._base_space)(center) self._center_lst[i] = center + flag = False except: - raise ValueError("The center of a disk approximating a Type IV point must \ - be padic or coerce into Qp, %s does not coerse into Qp" %(center)) + flag = True + if flag: + raise ValueError("The center of a disk approximating a Type IV point must " + \ + "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) + elif not isinstance(center.parent(),pAdicFieldGeneric): + try: + center = (self._base_space)(center) + except: + pass if (center.parent()).prime() != self._p: - raise ValueError("The center of a disk approximating a Type IV point of Berkovich \ - space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + raise ValueError("The center of a disk approximating a Type IV point of Berkovich " + \ + "space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): @@ -177,49 +204,76 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("The radius of a disk approximating a Type IV point must \ - coerce into the real numbers, %s does not coerce" %(radius)) + raise ValueError("The radius of a disk approximating a Type IV point must " + \ + "coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] dist = (center - previous_center).abs() if previous_radius < radius or dist > radius: - raise ValueError("Sequence of disks does not define a Type IV point as \ - containment is not proper") + raise ValueError("Sequence of disks does not define a Type IV point as " + \ + "containment is not proper") return else: - raise ValueError("bad value %s passed to child. Do not initialize Berkovich_Element_Cp \ - directly" %(child)) + raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ + "Berkovich_Element_Cp directly" ) return #the point must now be Type 1, 2, or 3, so we check that the center is of the appropriate type - if child == "affine" and error_check: - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - except: - raise ValueError("The center of a point of Affine Berkovich space over %s must convert \ - to %s" %(self._base_space,self._base_space)) - if (center.parent()).prime() != self._p: - raise ValueError("The center of a point of Berkovich space over Cp(%s) cannot be a point of %s")\ - %(self._p, center.parent()) - elif child == "projective" and error_check: - if not isinstance(center, SchemeMorphism_point_projective_field): - try: - center = (self._base_space)(center) - except: - raise ValueError("The center of a point of Projective Berkovich space must be a \ - point of P^1(Cp) or coerce into P^1(Qp)") #TODO change to Qpbar - if not(center.scheme().ambient_space() is center.scheme()): - raise ValueError("The center of a point of Projective Berkovich space cannot be \ - a point of %s" %(center.scheme())) - if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("The center of a point of Berkovich space over P^1(Cp(%s)) cannot \ - be a point of %s") %(self._p, center.scheme()) - elif child != "affine" and child != "projective": - raise ValueError("bad value %s passed to child. Do not initialize Berkovich_Element_Cp \ - directly" %(child)) + if error_check: + if child == "affine": + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("The center of a point of Affine Berkovich space over " + \ + "%s must convert to %s" %(self._base_space,self._base_space)) + elif not isinstance(center.parent(),pAdicFieldGeneric): + try: + center = (self._base_space)(center) + except: + pass + if (center.parent()).prime() != self._p: + raise ValueError("The center of a point of Berkovich space over " + \ + "Cp(%s) cannot be a point of %s" %(self._p, center.parent())) + elif child == "projective": + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("The center of a point of Projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s") %self._base_space + elif not isinstance(center.scheme().base_ring(), pAdicFieldGeneric): + if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("The center of a point of Projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s") %self._base_space + else: + try: + center = (self._base_space)(center) + except: + pass + if not(center.scheme().ambient_space() is center.scheme()): + raise ValueError("The center of a point of Projective Berkovich space cannot be " + \ + "a point of %s" %(center.scheme())) + if (center.scheme()).base_ring().prime() != self._p: + raise ValueError("The center of a point of Berkovich space over " + \ + "P^1(Cp(%s)) cannot be a point of %s" %(self._p, center.scheme())) + else: + raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ + "Berkovich_Element_Cp directly" ) self._center = center if (radius == None and power == None) or radius == 0: self._type = 1 @@ -230,14 +284,20 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if child == "projective": try: center.dehomogenize(1) + flag = False except ValueError: + flag = True + if flag: raise ValueError("Type II and III points cannot be centered at (1 : 0)") if power != None: if error_check: if power.parent() != QQ: try: power = QQ(power) + flag = False except: + flag = True + if flag: raise ValueError("power must convert to rationals") if radius != None: if radius != self._p**power: @@ -255,7 +315,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, try: radius = RR(radius) self._radius = radius + flag = False except: + flag = True + if flag: raise ValueError("Symbolic radius must be a real number") from sage.rings.real_mpfr import is_RealNumber if (not is_RealNumber(radius)) and power == None: @@ -277,6 +340,17 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._type = 3 self._power = None + def power(self): + """ + Power of ``p`` such that p^power = radius. + + For Type II points, always in QQ. For Type III points, + a real number. Not defined for Type I or IV points. + """ + if self._type in [1,4]: + raise AttributeError("Type I and IV points do not have a power") + return self._power + def radius(self): """ Radius of the corresponding disk (or sequence of disks) in ``Cp``. @@ -331,31 +405,61 @@ def diameter(self): def hyperbolic_metric(self, other): """ - Returns the hypebolic distance metric distance between ``self`` and ``other``. + The hyperbolic distance between this point and ``other``. + + Also known as the path distance metric, or the big metric. + See path_distance_metric for details. + + """ + return self.path_distance_metric(other) + + def big_metric(self, other): + """ + The big metric distance between this point and ``other``. + + Also known as the hyperbolic metric, or the path distance + metric. See path_distance_metric for details. + + """ + return self.path_distance_metric(other) + + def path_distance_metric(self, other): + """ + Returns the path distance metric distance between ``self`` and ``other``. + + Also referred to as the hyperbolic metric, or the big metric. + + On the set of Type II, III and IV points, the path distance metric + is a canonical metric. Following Baker and Rumely, we extend + the path distance metric to Type I points x,y by p(x,x) = 0 and p(x,y) = infty EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) sage: Q1 = B(1/4,4) sage: Q2 = B(1/4,6) - sage: Q1.hyperbolic_metric(Q2) + sage: Q1.path_distance_metric(Q2) 0.369070246428542 """ if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("Hyperbolic metric not defined between point of Berkovich space and %s" %(other)) + raise ValueError("Hyperbolic metric not defined between point of " + \ + "Berkovich space and %s" %(other)) if self.parent() != other.parent(): - raise ValueError("Input to hyperbolic metric was an element of a different Berkovich space") + raise ValueError("Input to hyperbolic metric was an element of a " + \ + "different Berkovich space") if self.type_of_point() == 1 or other.type_of_point() == 1: if self == other: return 0 else: - return "oo" - return 2*((self.join(other)).diameter().log(self.prime())) - self.diameter().log(self.prime()) - other.diameter().log(other.prime()) + return "infty" + return 2*((self.join(other)).diameter().log(self.prime()))\ + -self.diameter().log(self.prime())\ + -other.diameter().log(other.prime()) def small_metric(self, other): """ - Returns the small metric distance between ``self`` and ``other``. + Returns the small metric distance between this point and ``other``. EXAMPLES:: @@ -363,18 +467,36 @@ def small_metric(self, other): sage: Q1 = B(1/4,4) sage: Q2 = B(1/4,6) sage: Q1.small_metric(Q2) - 2.00000000000000 + 0.0833333333333333 """ if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("Hyperbolic metric not defined between point of Berkovich space and %s" %(other)) + raise ValueError("Hyperbolic metric not defined between point " + \ + "of Berkovich space and %s" %(other)) if self.parent() != other.parent(): - raise ValueError("Input to hyperbolic metric was an element of a different Berkovich space") - return 2*((self.join(other)).diameter()) - self.diameter() - other.diameter() + raise ValueError("Input to hyperbolic metric was an element " + \ + "of a different Berkovich space") + gauss = self.parent()(RR(0),RR(1)) + g_greater_than_s = gauss.partial_order(self) + g_greater_than_o = gauss.partial_order(other) + if g_greater_than_s and g_greater_than_o: + return 2*((self.join(other,gauss)).diameter()) - self.diameter() - other.diameter() + if not g_greater_than_s: + new_self = self.involution_map() + else: + new_self = self + if not g_greater_than_o: + new_other = other.involution_map() + else: + new_other = other + return 2*((new_self.join(new_other,gauss)).diameter()) \ + -new_self.diameter() \ + -new_other.diameter() + def Hsia_kernel_infinity(self, other): """ - Return the Hsia kernel at infinity evaluated at the point (``self``,``other``). + Return the Hsia kernel at infinity of this point with ``other``. EXAMPLES:: @@ -422,24 +544,35 @@ def _repr_(self): if self._type == 1: return "Type I point centered at " + format(self._center) elif self._type == 2: - return "Type II point centered at " + format(self._center) + " of radius %s^%s" %(self._p,self._power) + return "Type II point centered at " \ + + format(self._center) \ + + " of radius %s^%s" %(self._p,self._power) elif self._type == 3: - return "Type III point centered at " + format(self._center) + " of radius " + format(self._radius) + return "Type III point centered at " \ + + format(self._center) + " of radius " \ + + format(self._radius) else: if self._center_func != None and self._radius_func != None: - return "Type IV point of precision %s with centers given by %s and radii given by %s" %(self._prec, self._center_func,self._radius_func) + return "Type IV point of precision %s " %self._prec + \ + "with centers given by %s and radii given by %s"\ + %(self._center_func,self._radius_func) else: - return "Type IV point of precision %s, approximated by disks centered at %s ... with radii %s ..." %(self._prec, self._center_lst[:2],self._radius_lst[:2]) + return "Type IV point of precision %s, approximated " %self._prec + \ + "by disks centered at %s ... with radii %s ..." \ + %(self._center_lst[:2],self._radius_lst[:2]) def _latex_(self): from sage.misc.latex import latex if self._type == 1: text = r"the point %s of } \Bold{C}_%s" %(self._center, self._p) elif self._type in [2,3]: - text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" %(self._center, self._radius, self._p) + text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" \ + %(self._center, self._radius, self._p) else: - text = r"the sequence of disks with centers %s } \ldots \text{ and radii %s } \ldots" %(self._center_lst[:2],self._radius_lst[:2]) - return r"\text{Type %s Point of }" %(self._type) + latex(self.parent()) + r"\text{equivalent to " + text + text = r"the sequence of disks with centers %s } " %self._center_lst[:2] + \ + r"\ldots \text{ and radii %s } \ldots" %self._radius_lst[:2] + return r"\text{Type %s Point of }" %(self._type) \ + + latex(self.parent()) + r"\text{equivalent to " + text class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" @@ -546,11 +679,15 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: Q2=A(2.5,1); Q2 Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + - 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20) of radius 3^0 + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + + 3^18 + 3^19 + O(3^20) of radius 3^0 sage: Q5=A(3,0); Q5 Type I point centered at 3 + O(3^21) + sage: A(Zp(3)(2),2).center().parent() == A(Qp(3)(2),2).center().parent() + True + sage: Q1 == Q2 True @@ -592,9 +729,12 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check Element.__init__(self, parent) try: center.center().dehomogenize(1) + flag = False except: - raise ValueError("The point at infinity of Berkovich Projective space does not \ - convert to Berkovich Affine space") + flag = True + if flag: + raise ValueError("The point at infinity of Berkovich " + \ + "Projective space does not convert to Berkovich Affine space") self._center = center.center()[0] self._radius = center._radius self._power = center._power @@ -610,9 +750,9 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a \ - point of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(), \ - parent.base().prime())) + raise ValueError("Tried to convert from a point of Berkovich space " + \ + "over Cp(%s) to a " %center._base_space.base_ring().prime() + \ + "point of Berkovich space over Cp(%s)" %(parent.base().prime())) #otherwise we call the Berkovich_Element_Cp constructor with child=affine Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ @@ -646,6 +786,14 @@ def partial_order(self,other): """ The standard partial order on Berkovich space + Roughly, the partial order corresponds to containment of + the corresponding disks in ``Cp``. + + For example, let x and y be points of Type II or III. + If x has center ``c1`` and radius ``r1`` and y has center + ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) + is a subset of D(c2,r2) in ``Cp``. + INPUT: - ``other`` - another point of the same Berkovich space @@ -672,36 +820,47 @@ def partial_order(self,other): """ if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("partial order takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("partial order takes another point of " + \ + "the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("partial order takes another point of the same Berkovich Affine line") - if self.type_of_point() in [1,4]: + raise ValueError("partial order takes another point of " + \ + "the same Berkovich Affine line") + if self.type_of_point() == 1: if other.type_of_point() in [1,4]: if self == other: return True - else: - return None + return None else: - return False + s_less_than_o = other.partial_order(self) + if s_less_than_o == None: + return None + return not s_less_than_o else: - if other.type_of_point() in [1,4]: - return True + if other.type_of_point() == 1: + dist = (self.center() - other.center()).abs() + if dist <= self.radius(): + return True + return None + if other.type_of_point() == 4: + center = other.center()[len(other.center())] + dist = (self.center() - center).abs() + if dist <= self.radius(): + return True + return None else: dist = (self.center()-other.center()).abs() if(dist <= self.radius()): if(other.radius() <= self.radius()): return True - else: - return False + return False else: if(dist <= other.radius()): return False - else: - return None + return None def join(self, other, basepoint="infty"): """ - Computes the join of ``self`` and ``other`` with respect to ``basepoint``. + Computes the join of this point and ``other`` with respect to ``basepoint``. The join is first point that lies on the interesection of the path from self to basepoint and the path from other to @@ -744,40 +903,49 @@ def join(self, other, basepoint="infty"): Type III point centered at 2 + O(3^20) of radius 2.00000000000000 """ if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("join of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("join of a point of the Berkovich Affine line " + \ + "takes another point of the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("join takes two points in the same Berkovich Affine line") if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with Type IV points not implemented") + if basepoint == "infty": dist = (self.center() - other.center()).abs() return self.parent()(self.center(),max(dist,self.radius(),other.radius())) + else: if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise ValueError("basepoint for join must be a point of the Berkovich Affine line over Cp") + raise ValueError("basepoint for join must be a point of the Berkovich " + \ + "Affine line over Cp") if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") if basepoint.type_of_point() == 4: raise NotImplementedError("join not implemented for Type IV basepoint") + b_greater_than_s = basepoint.partial_order(self) b_greater_than_o = basepoint.partial_order(other) + if b_greater_than_s == None and b_greater_than_o == None: dist_b_s = (self.center() - basepoint.center()).abs() dist_b_o = (other.center() - basepoint.center()).abs() - return self.parent()(basepoint.center(),min(max(dist_b_o,other.radius(),basepoint.radius()),max(dist_b_s,self.radius(),basepoint.radius()))) + return self.parent()(basepoint.center(),\ + min(max(dist_b_o,other.radius(),basepoint.radius()),\ + max(dist_b_s,self.radius(),basepoint.radius()))) + elif b_greater_than_s != None and b_greater_than_o == None: dist_b_o = (other.center() - basepoint.center()).abs() - if dist_b_o > basepoint.radius() and dist_b_o < self.radius(): #TODO remove the check on basepoint.radius() + if dist_b_o < self.radius(): return (self.parent())(basepoint.center(),dist_b_o) - elif dist_b_o > basepoint.radius() and dist_b_o >= self.radius(): + elif dist_b_o >= self.radius(): if b_greater_than_s: return basepoint else: return self - else: - raise NotImplementedError("impossible case in join") #TODO remove this + elif b_greater_than_s == None and b_greater_than_o != None: return other.join(self,basepoint) + else: if b_greater_than_s: if b_greater_than_o: @@ -796,44 +964,147 @@ def join(self, other, basepoint="infty"): else: return self + def involution_map(self): + """ + Returns the image of this point under the involution map. + + The involution map is the extension of the map z |-> 1/z map + on ``Cp`` to Berkovich space. + + For Affine Berkovich Space, not defined for the Type I point centered at 0. + + EXAMPLES:: + + The involution map is 1/z on Type I points:: + + sage: B = Berkovich_Cp_Affine((3)) + sage: Q1 = B(1/2) + sage: Q1.involution_map() + Type I point centered at 2 + O(3^20) + + :: + + sage: Q2 = B(0,1/3) + sage: Q2.involution_map() + Type II point centered at 0 of radius 3^1 + + :: + + sage: Q3 = B(1/3,1/3) + sage: Q3.involution_map() + Type II point centered at 3 + O(3^21) of radius 3^-3 + + """ + parent = self.parent() + center = self.center() + + if self.type_of_point() == 1: + if center == 0: + raise ValueError("Involution map not deffined on Affine Type I point centered at 0") + return parent(1/center) + + zero = parent(QQ(0)) + radius = self.radius() + + if self.type_of_point() in [2,3]: + zero_contained_in_self = self.partial_order(zero) + if zero_contained_in_self: + if self.type_of_point() == 2: + power = self.power() + return parent(0,power=-power) + return parent(0,RR(1/radius)) + return parent(1/center,RR(radius/(center.abs()**2))) + + new_center_lst = [] + new_radius_lst = [] + for i in range(len(center)): + berk_point = parent(center[i],radius[i]) + zero_check = berk_point.partial_order(zero) + if zero_check: + new_center = 0 + new_radius = RR(1/radius[i]) + else: + new_center = 1/center[i] + new_radius = RR(radius[i]/(center[i].abs()**2)) + new_center_lst.append(new_center) + new_radius_lst.append(new_radius) + return parent(new_center_lst,new_radius_lst,error_check=False) + def potential_kernel(self, other, basepoint): """ - The potential kernel of ``self`` with ``other``, with basepoint ``basepoint``. + The potential kernel of this point with ``other``, with basepoint ``basepoint``. """ if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("potential kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("potential kernel of a point of the " + \ + "Berkovich Affine line takes another point of the Berkovich " + \ + "Affine line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("potential kernel takes two points in the same Berkovich Affine line") + raise ValueError("potential kernel takes two points in the " + \ + "same Berkovich Affine line") if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + raise ValueError("basepoint must be a point of the Berkovich " + \ + "Affine line over Cp") if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") - return basepoint.hyperbolic_metric((self.join(other,basepoint))) + return basepoint.path_distance_metric((self.join(other,basepoint))) def spherical_kernel(self,other): """ - The spherical kernel of ``self`` with ``other``. + The spherical kernel of this point with ``other``. + + The spherical kernel is one possible extension of + the spherical distance on P^1(``Cp``) to P^1 Berkovich. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2,9) + sage: Q2 = B(1/27,1/27) + sage: Q1.spherical_kernel(Q2) + 0.111111111111111 - Equivalently, the Hsia kernel with basepoint the Gauss - point. """ if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("spherical kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("spherical kernel of a point of the Berkovich Affine " + \ + "line takes another point of the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("spherical kernel takes two points in the same Berkovich Affine line") - gauss_point = (self.parent())(0,1) + gauss_point = (self.parent())(RR(0),RR(1)) w = self.join(other,gauss_point) - dist = gauss_point.hyperbolic_metric(w) + dist = gauss_point.path_distance_metric(w) if dist == "oo": return 0 return (self.prime())**(-1*dist) def Hsia_kernel(self, other, basepoint): """ - The Hsia kernel of ``self`` and ``other``, with basepoint ``basepoint``. + The Hsia kernel of this point and ``other``, with basepoint ``basepoint``. + + OUTPUT: If the basepoint is Type I and either this point or other equals + the basepoint, outputs the string "oo". Otherwise, a real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2,9) + sage: Q2 = B(1/27,1/27) + sage: Q3 = B(1,1/3) + sage: Q1.Hsia_kernel(Q2,Q3) + 0.111111111111111 + + :: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2,9) + sage: Q2 = B(1/2) + sage: Q3 = B(1/2) + sage: Q1.Hsia_kernel(Q2,Q3) + 'oo' + """ if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("Hsia kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("Hsia kernel of a point of the Berkovich Affine line " + \ + "takes another point of the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("Hsia kernel takes two points in the same Berkovich Affine line") if not isinstance(basepoint, Berkovich_Element_Cp_Affine): @@ -843,7 +1114,8 @@ def Hsia_kernel(self, other, basepoint): if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return "oo" - return (self.spherical_kernel(other))/(self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + return (self.spherical_kernel(other))/ \ + (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) def diameter(self, basepoint="oo"): """ @@ -853,7 +1125,7 @@ def diameter(self, basepoint="oo"): the limit of the radii of the corresponding disks in ``Cp``. If the basepoint is not infinity, the diameter - is the Hsia kernel of ``self`` with itself at + is the Hsia kernel of this point with itself at basepoint ``basepoint``. """ if basepoint == "oo": @@ -990,8 +1262,9 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("Tried to convert from a point of Berkovich space over Cp(%s) to a point \ - of Berkovich space over Cp(%s)" %(center._base_space.base_ring().prime(),parent.base().prime())) + raise ValueError("Tried to convert from a point of Berkovich space over " + \ + "Cp(%s) to a point of Berkovich space over Cp(%s)" \ + %(center._base_space.base_ring().prime(),parent.base().prime())) Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ prec=prec,child="projective",error_check=error_check) @@ -1024,8 +1297,8 @@ def __eq__(self, other): elif stype in [2,3] and otype in [2,3]: if self.radius() != other.radius(): return False - scent = self._field_center() - ocent = other._field_center() + scent = self.center()[0] + ocent = other.center()[0] center_dist = (scent - ocent).abs() return center_dist <= self.radius() else: @@ -1033,7 +1306,15 @@ def __eq__(self, other): def partial_order(self,other): """ - The standard partial order on Berkovich space + The standard partial order on Berkovich space. + + Roughly, the partial order corresponds to containment of + the corresponding disks in ``Cp``. + + For example, let x and y be points of Type II or III. + If x has center ``c1`` and radius ``r1`` and y has center + ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) + is a subset of D(c2,r2) in ``Cp``. INPUT: - ``other`` - another point of the same Berkovich space @@ -1061,36 +1342,47 @@ def partial_order(self,other): """ if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("partial order takes another point of the Berkovich Projective line, not %s" %(other)) + raise ValueError("partial order takes another point of " + \ + "the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("partial order takes another point of the same Berkovich Projective line") - if self.type_of_point() in [1,4]: + raise ValueError("partial order takes another point of the " + \ + "same Berkovich Projective line") + if self.type_of_point() == 1: if other.type_of_point() in [1,4]: if self == other: return True - else: - return None + return None else: - return False + s_less_than_o = other.partial_order(self) + if s_less_than_o == None: + return None + return not s_less_than_o else: - if other.type_of_point() in [1,4]: - return True + if other.type_of_point() == 1: + dist = (self.center()[0] - other.center()[0]).abs() + if dist <= self.radius(): + return True + return None + if other.type_of_point() == 4: + center = other.center()[len(other.center())] + dist = (self.center()[0] - center[0]).abs() + if dist <= self.radius(): + return True + return None else: - dist = (self._field_center()-other._field_center()).abs() + dist = (self.center()[0]-other.center()[0]).abs() if(dist <= self.radius()): if(other.radius() <= self.radius()): return True - else: - return False + return False else: if(dist <= other.radius()): return False - else: - return None + return None def join(self, other, basepoint="infty"): """ - Computes the join of ``self`` and ``other``, with respect to ``basepoint``. + Computes the join of this point and ``other``, with respect to ``basepoint``. The join is first point that lies on the interesection of the path from self to basepoint and the path from other to @@ -1133,23 +1425,30 @@ def join(self, other, basepoint="infty"): Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 """ if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("join of a point of the Berkovich Projective line takes another point of the Berkovich Projective line, not %s" %(other)) + raise ValueError("join of a point of the Berkovich Projective " + \ + "line takes another point of the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("join takes two points in the same Berkovich Projective line") if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with Type IV points not implemented") - infty = (self.parent())((1,0)) + + #we deal with the point at infinity as a special case + #if self, other and basepoint all covert to Affine Berkovich space, we do the join + #in Affine space and then convert back to projective + + parent = self.parent() + infty = parent((1,0)) if basepoint == "infty" or basepoint == infty: if self == infty or other == infty: return infty else: - dist = (self._field_center() - other._field_center()).abs() - return self.parent()(self.center(),max(dist,self.radius(),other.radius())) + dist = (self.center()[0] - other.center()[0]).abs() + return parent(self.center(),max(dist,self.radius(),other.radius())) else: if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint for join must be a point of the Berkovich Affine line over Cp") - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") + raise ValueError("basepoint for join must be a point of the Berkovich Projective line over Cp") + if basepoint.parent() != parent: + raise ValueError("basepoint must be a point of the same Berkovich Projective line") if basepoint.type_of_point() == 4: raise NotImplementedError("join not implemented for Type IV basepoint") if self == infty or other == infty: @@ -1167,58 +1466,161 @@ def join(self, other, basepoint="infty"): affine_other = B(other) affine_basepoint = B(basepoint) affine_join = affine_self.join(affine_other,affine_basepoint) - return (self.parent())(affine_join) + return (parent)(affine_join) + + def involution_map(self): + """ + Returns the image of this point under the involution map. + + The involution map is the extension of the map z |-> 1/z map + on projective space over ``Cp`` to Berkovich space. + + + EXAMPLES:: + + The involution map is 1/z on Type I points:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: Q1 = B(1/2) + sage: Q1.involution_map() + Type I point centered at (2 + O(3^20) : 1 + O(3^20)) + + :: + + sage: Q2 = B(0,1/3) + sage: Q2.involution_map() + Type II point centered at (0 : 1 + O(3^20)) of radius 3^1 + + :: + + sage: Q3 = B(1/3,1/3) + sage: Q3.involution_map() + Type II point centered at (3 + O(3^21) : 1 + O(3^20)) of radius 3^-3 + + """ + parent = self.parent() + center = self.center() + infty = parent((1,0)) + zero = parent(0) + + if self.type_of_point() == 1: + if self == infty: + return zero + if self == zero: + return infty + return parent(1/center[0]) + + radius = self.radius() + + if self.type_of_point() in [2,3]: + zero_contained_in_self = self.partial_order(zero) + if zero_contained_in_self: + if self.type_of_point() == 2: + power = self.power() + return parent(0,power=-power) + return parent(0,1/radius) + return parent(1/center[0],radius/(center[0].abs()**2)) + + new_center_lst = [] + new_radius_lst = [] + for i in range(len(center)): + berk_point = parent(center[i],radius[i]) + zero_check = berk_point.partial_order(zero) + if zero_check: + new_center = 0 + new_radius = 1/radius[i] + else: + new_center = 1/center[i][0] + new_radius = radius[i]/(center[i][0].abs()**2) + new_center_lst.append(new_center) + new_radius_lst.append(new_radius) + return parent(new_center_lst,new_radius_lst) def potential_kernel(self, other, basepoint): """ - The potential kernel of ``self`` with ``other``, + The potential kernel of this point with ``other``, with basepoint ``basepoint``. """ if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("potential kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("potential kernel of a point of the Berkovich " + \ + "Projective line takes another point of the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("potential kernel takes two points in the same Berkovich Affine line") + raise ValueError("potential kernel takes two points in the same Berkovich Projective line") if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + raise ValueError("basepoint must be a point of the Berkovich Projective line over Cp") if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") - return basepoint.hyperbolic_metric((self.join(other,basepoint))) + raise ValueError("basepoint must be a point of the same Berkovich Projective line") + return basepoint.path_distance_metric((self.join(other,basepoint))) def spherical_kernel(self,other): """ - The spherical kernel of ``self`` with ``other``. + The spherical kernel of this point with ``other``. + + The spherical kernel is one possible extension of the spherical + distance on P^1(``Cp``) to P^1 Berkovich. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2,2) + sage: Q2 = B(1/9,1) + sage: Q1.spherical_kernel(Q2) + 0.500000000000000 - Equivalently, the Hsia kernel with basepoint the Gauss - point. """ if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("spherical kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("spherical kernel of a point of the Berkovich Projective " + \ + "line takes another point of the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("spherical kernel takes two points in the same Berkovich Affine line") - gauss_point = (self.parent())(0,1) + raise ValueError("spherical kernel takes two points in the same Berkovich Projective line") + gauss_point = (self.parent())(RR(0),RR(1)) w = self.join(other,gauss_point) - dist = gauss_point.hyperbolic_metric(w) + dist = gauss_point.path_distance_metric(w) if dist == "oo": return 0 return (self.prime())**(-1*dist) def Hsia_kernel(self, other, basepoint): """ - The Hsia kernel of ``self`` and ``other``, + The Hsia kernel of this point and ``other``, with basepoint ``basepoint`` + + OUTPUT: If the basepoint is Type I and either this point or other equals + the basepoint, outputs the string "oo". Otherwise, a real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2,9) + sage: Q2 = B(1/27,1/27) + sage: Q3 = B(1,1/3) + sage: Q1.Hsia_kernel(Q2,Q3) + 0.111111111111111 + + :: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2,9) + sage: Q2 = B(1/2) + sage: Q3 = B(1/2) + sage: Q1.Hsia_kernel(Q2,Q3) + 'oo' + """ if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("Hsia kernel of a point of the Berkovich Affine line takes another point of the Berkovich Affine line, not %s" %(other)) + raise ValueError("Hsia kernel of a point of the Berkovich Projective line " + \ + "takes another point of the Berkovich Projective line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("Hsia kernel takes two points in the same Berkovich Affine line") + raise ValueError("Hsia kernel takes two points in the same Berkovich Projective line") if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") + raise ValueError("basepoint must be a point of the Berkovich Projective line over Cp") if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") + raise ValueError("basepoint must be a point of the same Berkovich Projective line") if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return "oo" - return (self.spherical_kernel(other))/(self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + return (self.spherical_kernel(other))/ \ + (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) def diameter(self, basepoint="oo"): """ @@ -1228,7 +1630,7 @@ def diameter(self, basepoint="oo"): the limit of the radii of the corresponding disks in ``Cp``. If the basepoint is not infinity, the diameter - is the Hsia kernel of ``self`` with itself at + is the Hsia kernel of this point with itself at basepoint ``basepoint``. """ if basepoint == "oo": @@ -1306,7 +1708,8 @@ def _coerce_map_from_(self,S): return False def _repr_(self): - return "Affine Berkovich line over Cp(%s) of precision %s" %(self.prime(),self.base().precision_cap()) + return "Affine Berkovich line over Cp(%s) of precision %s" \ + %(self.prime(),self.base().precision_cap()) def _latex_(self): return r"\text{Affine Berkovich line over } \Bold{C}_{%s}" %(self.prime()) @@ -1362,12 +1765,14 @@ def __init__(self,base): raise ValueError("Base of Projective Berkovich Space must be Projective Space") from sage.rings.padics.generic_nodes import is_pAdicField if not (is_pAdicField(base.base_ring())): - raise ValueError("Base of Projective Berkovich Space must be Projective Space over Qp") + raise ValueError("Base of Projective Berkovich Space must be " + \ + "Projective Space over Qp") if base.ambient_space() != base: - raise ValueError("Base of Projective Berkovich Space must be Projective Space over Qp") + raise ValueError("Base of Projective Berkovich Space must be " + \ + "Projective Space over Qp") if base.dimension() != 1: - raise ValueError("Base of Projective Berkovich Space must be Projective Space of \ - dimension 1 over Qp") + raise ValueError("Base of Projective Berkovich Space must be " + \ + "Projective Space of dimension 1 over Qp") self._p = base.base_ring().prime() Parent.__init__(self, base = base, category=TopologicalSpaces()) From e0e2764e7393d40900405b40a3861038e0e4a40c Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 17 Jun 2020 14:12:13 -0400 Subject: [PATCH 007/379] 29844: Fixed partial order of infinity + cleaned up partial order and join code --- src/sage/schemes/berkovich/berkovich_space.py | 232 ++++++++---------- 1 file changed, 108 insertions(+), 124 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index e8930a98bb6..b86017a6660 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -34,7 +34,6 @@ class Berkovich_Element_Cp(Berkovich_Element): """ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, error_check=True): - Element.__init__(self, parent) from sage.rings.function_field.element import is_FunctionFieldElement from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.fraction_field_element import FractionFieldElement_1poly_field @@ -720,13 +719,13 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space #unless we are passed a point of projective Berkovich space + Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() #if this is a point of projective berkovich space, we do the conversion if isinstance(center, Berkovich_Element_Cp_Projective): if (center.prime() == self._p): - Element.__init__(self, parent) try: center.center().dehomogenize(1) flag = False @@ -754,7 +753,6 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check "over Cp(%s) to a " %center._base_space.base_ring().prime() + \ "point of Berkovich space over Cp(%s)" %(parent.base().prime())) - #otherwise we call the Berkovich_Element_Cp constructor with child=affine Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ prec=prec,child="affine",error_check=error_check) @@ -810,53 +808,35 @@ def partial_order(self,other): sage: Q1.partial_order(Q2) False + :: + sage: Q3 = B(1/2) sage: Q1.partial_order(Q3) True + :: + sage: Q4 = B(1/81,1) sage: print(Q4.partial_order(Q1)) None + :: + + sage: print(Q4.partial_order(Q3)) + None """ + #error check, then convert to projective berkovich space to do the partial order if not isinstance(other,Berkovich_Element_Cp_Affine): raise ValueError("partial order takes another point of " + \ "the Berkovich Affine line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("partial order takes another point of " + \ "the same Berkovich Affine line") - if self.type_of_point() == 1: - if other.type_of_point() in [1,4]: - if self == other: - return True - return None - else: - s_less_than_o = other.partial_order(self) - if s_less_than_o == None: - return None - return not s_less_than_o - else: - if other.type_of_point() == 1: - dist = (self.center() - other.center()).abs() - if dist <= self.radius(): - return True - return None - if other.type_of_point() == 4: - center = other.center()[len(other.center())] - dist = (self.center() - center).abs() - if dist <= self.radius(): - return True - return None - else: - dist = (self.center()-other.center()).abs() - if(dist <= self.radius()): - if(other.radius() <= self.radius()): - return True - return False - else: - if(dist <= other.radius()): - return False - return None + base = self._base_space + B = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) + proj_self = B(self) + proj_other = B(other) + return proj_self.partial_order(proj_other) def join(self, other, basepoint="infty"): """ @@ -901,7 +881,9 @@ def join(self, other, basepoint="infty"): sage: Q7 = B(1/27,1/27) sage: Q1.join(Q7,Q2) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + """ + #we error check and then pass to projective space to do the join if not isinstance(other,Berkovich_Element_Cp_Affine): raise ValueError("join of a point of the Berkovich Affine line " + \ "takes another point of the Berkovich Affine line, not %s" %(other)) @@ -910,59 +892,25 @@ def join(self, other, basepoint="infty"): if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with Type IV points not implemented") - if basepoint == "infty": - dist = (self.center() - other.center()).abs() - return self.parent()(self.center(),max(dist,self.radius(),other.radius())) + parent = self.parent() + base = self._base_space + B = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) + proj_self = B(self) + proj_other = B(other) - else: - if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise ValueError("basepoint for join must be a point of the Berkovich " + \ - "Affine line over Cp") - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") - if basepoint.type_of_point() == 4: - raise NotImplementedError("join not implemented for Type IV basepoint") - - b_greater_than_s = basepoint.partial_order(self) - b_greater_than_o = basepoint.partial_order(other) - - if b_greater_than_s == None and b_greater_than_o == None: - dist_b_s = (self.center() - basepoint.center()).abs() - dist_b_o = (other.center() - basepoint.center()).abs() - return self.parent()(basepoint.center(),\ - min(max(dist_b_o,other.radius(),basepoint.radius()),\ - max(dist_b_s,self.radius(),basepoint.radius()))) - - elif b_greater_than_s != None and b_greater_than_o == None: - dist_b_o = (other.center() - basepoint.center()).abs() - if dist_b_o < self.radius(): - return (self.parent())(basepoint.center(),dist_b_o) - elif dist_b_o >= self.radius(): - if b_greater_than_s: - return basepoint - else: - return self + if basepoint == "infty": + return parent(proj_self.join(proj_other)) - elif b_greater_than_s == None and b_greater_than_o != None: - return other.join(self,basepoint) + if not isinstance(basepoint, Berkovich_Element_Cp_Affine): + raise ValueError("basepoint for join must be a point of the Berkovich " + \ + "Affine line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich Affine line") + if basepoint.type_of_point() == 4: + raise NotImplementedError("join not implemented for Type IV basepoint") + proj_basepoint = B(basepoint) + return parent(proj_self.join(proj_other, proj_basepoint)) - else: - if b_greater_than_s: - if b_greater_than_o: - if self.partial_order(other): - return self - else: - return other - else: - return basepoint - else: - if b_greater_than_o: - return basepoint - else: - if self.partial_order(other): - return other - else: - return self def involution_map(self): """ @@ -971,7 +919,8 @@ def involution_map(self): The involution map is the extension of the map z |-> 1/z map on ``Cp`` to Berkovich space. - For Affine Berkovich Space, not defined for the Type I point centered at 0. + For Affine Berkovich Space, not defined for the Type I + point centered at 0. EXAMPLES:: @@ -1240,13 +1189,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check #if we are given a point of Affine Berkovich Space, we do the conversion #otherwise we call the Berkovich_Element_Cp constructor with child="projective" + Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() #conversion from Affine points is handled in this constructor if isinstance(center, Berkovich_Element_Cp_Affine): if (center.prime() == self._p): - Element.__init__(self, parent) self._center = (self._base_space)(center._center) self._radius = center._radius self._power = center._power @@ -1269,15 +1218,6 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ prec=prec,child="projective",error_check=error_check) - def _field_center(self): - """ - Returns the center of ``self`` as an element - of a field, allowing for arithmetic. - Used to make handling Type II and III points - easier, as the centers of these points are never (1 : 0). - """ - return self.center()[0] - def __eq__(self, other): """ Equality operator. @@ -1347,6 +1287,18 @@ def partial_order(self,other): if self.parent() != other.parent(): raise ValueError("partial order takes another point of the " + \ "same Berkovich Projective line") + + #if self or other is infinity, we apply the involution map + parent = self.parent() + infty = parent((1,0)) + zero = parent(0) + if self == infty or other == infty: + if self == zero or other == zero: + return None + newself = self.involution_map() + newother = other.involution_map() + return newself.partial_order(newother) + if self.type_of_point() == 1: if other.type_of_point() in [1,4]: if self == other: @@ -1424,49 +1376,82 @@ def join(self, other, basepoint="infty"): sage: Q1.join(Q7,Q2) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 """ + parent = self.parent() + if not isinstance(other,Berkovich_Element_Cp_Projective): raise ValueError("join of a point of the Berkovich Projective " + \ "line takes another point of the Berkovich Projective line, not %s" %(other)) - if self.parent() != other.parent(): + if other.parent() != parent: raise ValueError("join takes two points in the same Berkovich Projective line") if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with Type IV points not implemented") #we deal with the point at infinity as a special case - #if self, other and basepoint all covert to Affine Berkovich space, we do the join - #in Affine space and then convert back to projective - parent = self.parent() infty = parent((1,0)) + if basepoint == "infty" or basepoint == infty: if self == infty or other == infty: return infty - else: - dist = (self.center()[0] - other.center()[0]).abs() - return parent(self.center(),max(dist,self.radius(),other.radius())) - else: - if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint for join must be a point of the Berkovich Projective line over Cp") - if basepoint.parent() != parent: - raise ValueError("basepoint must be a point of the same Berkovich Projective line") - if basepoint.type_of_point() == 4: - raise NotImplementedError("join not implemented for Type IV basepoint") - if self == infty or other == infty: - if self == other: - return infty + dist = (self.center()[0] - other.center()[0]).abs() + return parent(self.center(),max(dist,self.radius(),other.radius())) #TODO optimize for Type II + + if not isinstance(basepoint, Berkovich_Element_Cp_Projective): + raise ValueError("basepoint for join must be a point of the Berkovich Projective line over Cp") + if basepoint.parent() != parent: + raise ValueError("basepoint must be a point of the same Berkovich Projective line") + if basepoint.type_of_point() == 4: + raise NotImplementedError("join not implemented for Type IV basepoint") + + if self == infty: + return other.join(basepoint) + if other == infty: + return self.join(basepoint) + + #since none of the self, other, and basepoint are infinity, we can now treat them + #as affine points + + b_greater_than_s = basepoint.partial_order(self) + b_greater_than_o = basepoint.partial_order(other) + + if b_greater_than_s == None and b_greater_than_o == None: + dist_b_s = (self.center()[0] - basepoint.center()[0]).abs() + dist_b_o = (other.center()[0] - basepoint.center()[0]).abs() + return parent(basepoint.center(),\ + min(max(dist_b_o,other.radius(),basepoint.radius()),\ + max(dist_b_s,self.radius(),basepoint.radius()))) + + elif b_greater_than_s != None and b_greater_than_o == None: + dist_b_o = (other.center()[0] - basepoint.center()[0]).abs() + if dist_b_o < self.radius(): + return parent(basepoint.center(),dist_b_o) + elif dist_b_o >= self.radius(): + if b_greater_than_s: + return basepoint else: - if self == infty: - return other.join(basepoint) + return self + + elif b_greater_than_s == None and b_greater_than_o != None: + return other.join(self,basepoint) + + else: + if b_greater_than_s: + if b_greater_than_o: + if self.partial_order(other): + return self else: - return self.join(basepoint) + return other + else: + return basepoint else: - base = self._base_space.base_ring() - B = Berkovich_Cp_Affine(base) - affine_self = B(self) - affine_other = B(other) - affine_basepoint = B(basepoint) - affine_join = affine_self.join(affine_other,affine_basepoint) - return (parent)(affine_join) + if b_greater_than_o: + return basepoint + else: + if self.partial_order(other): + return other + else: + return self + def involution_map(self): """ @@ -1475,7 +1460,6 @@ def involution_map(self): The involution map is the extension of the map z |-> 1/z map on projective space over ``Cp`` to Berkovich space. - EXAMPLES:: The involution map is 1/z on Type I points:: From fa1234e2e23f376f719f3d245d698d1b08b380bc Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 17 Jun 2020 19:18:27 -0400 Subject: [PATCH 008/379] 29844: Added output/input blocks to most functions --- src/sage/schemes/berkovich/berkovich_space.py | 293 ++++++++++++++++-- 1 file changed, 269 insertions(+), 24 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index b86017a6660..bb683ebc796 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -331,13 +331,49 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._power = power self._type = 2 return - power = radius.log(self._p) + power = RR(radius.log(self._p)) if power.is_integer(): self._power = QQ(power) self._type = 2 else: self._type = 3 - self._power = None + self._power = power + + def prec(self): + """ + Returns the precision of a Type IV point. + + Not defined for Type I, II or III points. + + OUTPUT: An integer + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: d = B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]) + sage: d.prec() + 3 + """ + return self.precision() + + def precision(self): + """ + Returns the precision of a Type IV point. + + Not defined for Type I, II, or III points. + + OUTPUT: An integer + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) + sage: d.prec() + 3 + """ + if self._type in [1,2,3]: + raise AttributeError("Type I, II, and III points do not have a precision") + return self._prec def power(self): """ @@ -345,6 +381,24 @@ def power(self): For Type II points, always in QQ. For Type III points, a real number. Not defined for Type I or IV points. + + OUTPUT: + + - An integer for Type II points + - A real number for Type III points + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1, 9) + sage: Q1.power() + 2 + + :: + + sage: Q2 = B(1,4) + sage: Q2.power() + 1.26185950714291 """ if self._type in [1,4]: raise AttributeError("Type I and IV points do not have a power") @@ -356,8 +410,21 @@ def radius(self): OUTPUT: - - For Type I, II, and III points, returns a list of a single non-negative - real number. For Type IV points, returns a list of non-negative real numbers. + - A non-negative real number for points Types I-III + - A list of non-negative real numbers for Type IV points + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1, 2/5) + sage: Q1.radius() + 0.400000000000000 + + :: + + sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) + sage: d.radius() + [1.76100000000000, 1.12300000000000, 1.11200000000000] """ if self._type == 4: return self._radius_lst[:] @@ -368,12 +435,15 @@ def diameter(self): Diameter function on Berkovich space. For Type I, II, and III points, returns the radius. + For Type IV points returns either the last radius in the finite approximation, or if a generating function was given for the radii, the diameter is computed as the limit of the function as it's variable tends to infinity. + OUTPUT: A real number. + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -381,10 +451,21 @@ def diameter(self): sage: Q1.diameter() 0 + :: + sage: Q2 = B(1/2,9) sage: Q2.diameter() 9.00000000000000 + The diameter of a Type IV point is the limit of the radii:: + + sage: R. = PolynomialRing(Qp(3)) + sage: f = R(2) + sage: S. = PolynomialRing(RR) + sage: S = FractionField(S) + sage: g = (y+1)/y + sage: B(f,g).diameter() + 1.0 """ if self._type == 4: if self._radius_func == None: @@ -408,7 +489,6 @@ def hyperbolic_metric(self, other): Also known as the path distance metric, or the big metric. See path_distance_metric for details. - """ return self.path_distance_metric(other) @@ -429,9 +509,15 @@ def path_distance_metric(self, other): Also referred to as the hyperbolic metric, or the big metric. On the set of Type II, III and IV points, the path distance metric - is a canonical metric. Following Baker and Rumely, we extend + is a metric. Following Baker and Rumely, we extend the path distance metric to Type I points x,y by p(x,x) = 0 and p(x,y) = infty + INPUT: + + - ``other`` - a point of the same Berkovich space + + OUTPUT: A real number, or the infinity symbol 'oo' + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -440,18 +526,23 @@ def path_distance_metric(self, other): sage: Q1.path_distance_metric(Q2) 0.369070246428542 + :: + + sage: Q3 = B(1) + sage: Q3.path_distance_metric(Q1) + 'oo' """ if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("Hyperbolic metric not defined between point of " + \ + raise ValueError("Path distance metric not defined between point of " + \ "Berkovich space and %s" %(other)) if self.parent() != other.parent(): - raise ValueError("Input to hyperbolic metric was an element of a " + \ + raise ValueError("Input to path distance metric was an element of a " + \ "different Berkovich space") if self.type_of_point() == 1 or other.type_of_point() == 1: if self == other: return 0 else: - return "infty" + return "oo" return 2*((self.join(other)).diameter().log(self.prime()))\ -self.diameter().log(self.prime())\ -other.diameter().log(other.prime()) @@ -460,6 +551,12 @@ def small_metric(self, other): """ Returns the small metric distance between this point and ``other``. + INPUT: + + - ``other`` - a point of the same Berkovich space + + OUTPUT: A real number + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -497,6 +594,12 @@ def Hsia_kernel_infinity(self, other): """ Return the Hsia kernel at infinity of this point with ``other``. + INPUT: + + - ``other`` - a point of the same Berkovich space + + OUTPUT: A real number + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -510,12 +613,7 @@ def Hsia_kernel_infinity(self, other): def center(self): """ - Returns the center of the corresponding disk (or sequence of disks) in ``Cp`` - - OUTPUT: - - - For Type I, II, and III points, returns an element of ``Qp`` or a finite extension. For - Type IV points, returns a list of points of ``Qp`` or a finite extension + Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. """ if self._type == 4: return self._center_lst[:] @@ -524,12 +622,16 @@ def center(self): def type_of_point(self): """ Returns the Type of this point of Berkovich space over ``Cp`` + + OUTPUT: An integer between 1 and 4 inclusive """ return self._type def prime(self): """ Shorthand for residue characteristic of the parent + + OUTPUT: A prime integer """ return self._p @@ -756,6 +858,30 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ prec=prec,child="affine",error_check=error_check) + def center(self): + """ + Returns the center of the corresponding disk (or sequence of disks) in ``Cp`` + + OUTPUT: + + - For Type I-III points, a point of ``Cp`` + - For Type IV points, a list of points of ``Cp`` + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(2,1) + sage: Q1.center() + 2 + O(3^20) + + :: + + sage: d = B([4,2],[4,2]) + sage: d.center() + [1 + 3 + O(3^20), 2 + O(3^20)] + """ + return super().center() + def __eq__(self, other): """ Equality operator. @@ -793,9 +919,11 @@ def partial_order(self,other): is a subset of D(c2,r2) in ``Cp``. INPUT: + - ``other`` - another point of the same Berkovich space OUTPUT: + - ``True`` - If self > other in the standard partial order - ``False`` - If self < other in the standard partial order - ``None`` - If the two points are not comparable @@ -852,6 +980,8 @@ def join(self, other, basepoint="infty"): - ``basepoint`` -- (default: Infinity) the basepoint with which to take the join with respect to + OUTPUT: A point of the same Berkovich space + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -922,6 +1052,8 @@ def involution_map(self): For Affine Berkovich Space, not defined for the Type I point centered at 0. + OUTPUT: A point of the same Berkovich space + EXAMPLES:: The involution map is 1/z on Type I points:: @@ -982,6 +1114,22 @@ def involution_map(self): def potential_kernel(self, other, basepoint): """ The potential kernel of this point with ``other``, with basepoint ``basepoint``. + + INPUT: + + - ``other`` - the point with which to take the potential kernel + - ``basepoint`` - the basepoint with which to take the potential kernel + + OUTPUT: A real number or the infinity symbol 'oo' + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(27,1) + sage: Q2 = B(1/3,2) + sage: Q3 = B(1/9,1/2) + sage: Q3.potential_kernel(Q1,Q2) + 0.369070246428543 """ if not isinstance(other,Berkovich_Element_Cp_Affine): raise ValueError("potential kernel of a point of the " + \ @@ -1004,6 +1152,8 @@ def spherical_kernel(self,other): The spherical kernel is one possible extension of the spherical distance on P^1(``Cp``) to P^1 Berkovich. + OUTPUT: A real number + EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) @@ -1029,8 +1179,12 @@ def Hsia_kernel(self, other, basepoint): """ The Hsia kernel of this point and ``other``, with basepoint ``basepoint``. - OUTPUT: If the basepoint is Type I and either this point or other equals - the basepoint, outputs the string "oo". Otherwise, a real number. + INPUT: + + - ``other`` - The point with which to take the Hsia kernel with + - ``basepoint`` - The basepoint of the Hsia kernel + + OUTPUT: A real number or the infinity symbol 'oo' EXAMPLES:: @@ -1076,6 +1230,13 @@ def diameter(self, basepoint="oo"): If the basepoint is not infinity, the diameter is the Hsia kernel of this point with itself at basepoint ``basepoint``. + + INPUT: + + - ``basepoint`` - (default = Infinity) the basepoint of + the generalized diameter + + OUTPUT: A real number or the infinity symbol 'oo' """ if basepoint == "oo": return super().diameter() @@ -1218,6 +1379,30 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ prec=prec,child="projective",error_check=error_check) + def center(self): + """ + Returns the center of the corresponding disk (or sequence of disks) in P^1(Cp) + + OUTPUT: + + - For Type I-III points, a point of P^1(Cp) + - For Type IV points, a list of points of P^1(Cp) + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2,1) + sage: Q1.center() + (2 + O(3^20) : 1 + O(3^20)) + + :: + + sage: d = B([4,2],[4,2]) + sage: d.center() + [(1 + 3 + O(3^20) : 1 + O(3^20)), (2 + O(3^20) : 1 + O(3^20))] + """ + return super().center() + def __eq__(self, other): """ Equality operator. @@ -1272,14 +1457,24 @@ def partial_order(self,other): sage: Q1.partial_order(Q2) False + :: + sage: Q3 = B(1/2) sage: Q1.partial_order(Q3) True + :: + sage: Q4 = B(1/81,1) sage: print(Q4.partial_order(Q1)) None + We check infinity works in the partial order:: + + sage: Q5 = B((1,0)) + sage: Q6 = B(3,3) + sage: Q6.partial_order(Q5) + True """ if not isinstance(other,Berkovich_Element_Cp_Projective): raise ValueError("partial order takes another point of " + \ @@ -1346,6 +1541,8 @@ def join(self, other, basepoint="infty"): - ``basepoint`` -- (default: Infinity) the basepoint with which to take the join with respect to + OUTPUT: A point of the same Berkovich space + EXAMPLES:: sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3),1)) @@ -1383,8 +1580,17 @@ def join(self, other, basepoint="infty"): "line takes another point of the Berkovich Projective line, not %s" %(other)) if other.parent() != parent: raise ValueError("join takes two points in the same Berkovich Projective line") - if self.type_of_point() == 4 or other.type_of_point() == 4: - raise NotImplementedError("join with Type IV points not implemented") + + #if either self or other is Type IV, we use the last disk in the approximation + + if self.type_of_point() == 4: + new_center = self.center()[self.prec()-1] + new_radius = self.radius()[self.prec()-1] + return parent(new_center,new_radius).join(other) + if other.type_of_point() == 4: + new_center = other.center()[other.prec()-1] + new_radius = other.radius()[other.prec()-1] + return self.join(parent(new_center,new_radius)) #we deal with the point at infinity as a special case @@ -1400,8 +1606,13 @@ def join(self, other, basepoint="infty"): raise ValueError("basepoint for join must be a point of the Berkovich Projective line over Cp") if basepoint.parent() != parent: raise ValueError("basepoint must be a point of the same Berkovich Projective line") + + #if the basepoint is Type IV, we use the last disk in the approximation + if basepoint.type_of_point() == 4: - raise NotImplementedError("join not implemented for Type IV basepoint") + new_center = other.center()[other.prec()-1] + new_radius = other.radius()[other.prec()-1] + return self.join(other, parent(new_center,new_radius)) if self == infty: return other.join(basepoint) @@ -1452,7 +1663,6 @@ def join(self, other, basepoint="infty"): else: return self - def involution_map(self): """ Returns the image of this point under the involution map. @@ -1460,6 +1670,8 @@ def involution_map(self): The involution map is the extension of the map z |-> 1/z map on projective space over ``Cp`` to Berkovich space. + OUTPUT: A point of the same Berkovich space + EXAMPLES:: The involution map is 1/z on Type I points:: @@ -1524,6 +1736,22 @@ def potential_kernel(self, other, basepoint): """ The potential kernel of this point with ``other``, with basepoint ``basepoint``. + + INPUT: + + - ``other`` - the point with which to take the potential kernel + - ``basepoint`` - the basepoint with which to take the potential kernel + + OUTPUT: A real number or the infinity symbol 'oo' + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(27,1) + sage: Q2 = B(1/3,2) + sage: Q3 = B(1/9,1/2) + sage: Q3.potential_kernel(Q1,Q2) + 0.369070246428543 """ if not isinstance(other,Berkovich_Element_Cp_Projective): raise ValueError("potential kernel of a point of the Berkovich " + \ @@ -1543,6 +1771,12 @@ def spherical_kernel(self,other): The spherical kernel is one possible extension of the spherical distance on P^1(``Cp``) to P^1 Berkovich. + INPUT: + + - ``other`` - The point with which to take the spherical kernel + + OUTPUT: A real number + EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) @@ -1567,10 +1801,14 @@ def spherical_kernel(self,other): def Hsia_kernel(self, other, basepoint): """ The Hsia kernel of this point and ``other``, - with basepoint ``basepoint`` + with basepoint ``basepoint``. + + INPUT:: + + - ``other`` - the point with which to take the Hsia kernel + - ``basepoint`` - the basepoint with which to take the Hsia kernel - OUTPUT: If the basepoint is Type I and either this point or other equals - the basepoint, outputs the string "oo". Otherwise, a real number. + OUTPUT: A real number or the infinity symbol 'oo' EXAMPLES:: @@ -1616,6 +1854,13 @@ def diameter(self, basepoint="oo"): If the basepoint is not infinity, the diameter is the Hsia kernel of this point with itself at basepoint ``basepoint``. + + INPUT: + + - ``basepoint`` - (default = Infinity) the basepoint of + the generalized diameter + + OUTPUT: A real number or the infinity symbol 'oo' """ if basepoint == "oo": return super().diameter() From 147b7725861ab171b722091bbf37a2e609f3b61e Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 18 Jun 2020 15:33:23 -0400 Subject: [PATCH 009/379] 29844: fixed bug in join with general basepoint --- src/sage/schemes/berkovich/berkovich_space.py | 160 +++++++++++------- 1 file changed, 98 insertions(+), 62 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index bb683ebc796..fdbd5b0c691 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -8,8 +8,9 @@ from sage.categories.topological_spaces import TopologicalSpaces from sage.symbolic.expression import is_Expression from sage.rings.real_mpfr import RR -from sage.rings.padics.generic_nodes import pAdicFieldGeneric from sage.rings.padics.padic_generic_element import pAdicGenericElement +from sage.rings.padics.padic_base_generic import pAdicBaseGeneric +from sage.rings.padics.generic_nodes import is_pAdicField from sage.rings.padics.factory import Qp from sage.schemes.projective.projective_space import ProjectiveSpace from sage.schemes.projective.projective_point import SchemeMorphism_point_projective_field @@ -129,7 +130,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if flag: raise ValueError("The center of a point of Projective Berkovich" + \ "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) - elif not isinstance(center.scheme().base_ring(), pAdicFieldGeneric): + elif not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(),pAdicGeneric): try: center = (self._base_space)(center) @@ -187,7 +188,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if flag: raise ValueError("The center of a disk approximating a Type IV point must " + \ "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) - elif not isinstance(center.parent(),pAdicFieldGeneric): + elif not is_pAdicField(center.parent()): try: center = (self._base_space)(center) except: @@ -231,7 +232,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if flag: raise ValueError("The center of a point of Affine Berkovich space over " + \ "%s must convert to %s" %(self._base_space,self._base_space)) - elif not isinstance(center.parent(),pAdicFieldGeneric): + elif not is_pAdicField(center.parent()): try: center = (self._base_space)(center) except: @@ -249,7 +250,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if flag: raise ValueError("The center of a point of Projective Berkovich space must be a " + \ "point of P^1(Cp) or coerce into %s") %self._base_space - elif not isinstance(center.scheme().base_ring(), pAdicFieldGeneric): + elif not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: center = (self._base_space)(center) @@ -514,7 +515,7 @@ def path_distance_metric(self, other): INPUT: - - ``other`` - a point of the same Berkovich space + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number, or the infinity symbol 'oo' @@ -553,7 +554,7 @@ def small_metric(self, other): INPUT: - - ``other`` - a point of the same Berkovich space + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number @@ -596,7 +597,7 @@ def Hsia_kernel_infinity(self, other): INPUT: - - ``other`` - a point of the same Berkovich space + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number @@ -675,6 +676,15 @@ def _latex_(self): return r"\text{Type %s Point of }" %(self._type) \ + latex(self.parent()) + r"\text{equivalent to " + text + def __hash__(self): + """ + Return the hash of this point. + """ + if self.type_of_point() != 4: + return hash(str(self.center())+str(self.radius())) + return hash(str(self.center()[self.precision()])+str(self.radius()[self.precision()])) + + class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" Element class of the Berkovich affine line over `\CC_p` @@ -906,6 +916,12 @@ def __eq__(self, other): else: return False + def __hash__(self): + """ + Returns the hash of this point. + """ + return super().__hash__() + def partial_order(self,other): """ The standard partial order on Berkovich space @@ -920,7 +936,7 @@ def partial_order(self,other): INPUT: - - ``other`` - another point of the same Berkovich space + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: @@ -976,9 +992,9 @@ def join(self, other, basepoint="infty"): INPUT: - - ``other`` -- the second point with which to take the join - - ``basepoint`` -- (default: Infinity) the basepoint with which - to take the join with respect to + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- (default: Infinity) A point of the same + Berkovich space as this point or the string 'infty' OUTPUT: A point of the same Berkovich space @@ -1117,8 +1133,8 @@ def potential_kernel(self, other, basepoint): INPUT: - - ``other`` - the point with which to take the potential kernel - - ``basepoint`` - the basepoint with which to take the potential kernel + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1181,8 +1197,8 @@ def Hsia_kernel(self, other, basepoint): INPUT: - - ``other`` - The point with which to take the Hsia kernel with - - ``basepoint`` - The basepoint of the Hsia kernel + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1233,8 +1249,8 @@ def diameter(self, basepoint="oo"): INPUT: - - ``basepoint`` - (default = Infinity) the basepoint of - the generalized diameter + - ``basepoint`` -- (default = Infinity) A point of the + same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' """ @@ -1429,6 +1445,12 @@ def __eq__(self, other): else: return False + def __hash__(self): + """ + Returns the hash of this point. + """ + return super().__hash__() + def partial_order(self,other): """ The standard partial order on Berkovich space. @@ -1442,7 +1464,7 @@ def partial_order(self,other): is a subset of D(c2,r2) in ``Cp``. INPUT: - - ``other`` - another point of the same Berkovich space + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: - ``True`` - If self > other in the standard partial order @@ -1537,9 +1559,9 @@ def join(self, other, basepoint="infty"): INPUT: - - ``other`` -- the second point with which to take the join - - ``basepoint`` -- (default: Infinity) the basepoint with which - to take the join with respect to + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- (default: Infinity) A point of the same + Berkovich space as this point or the string 'infty' OUTPUT: A point of the same Berkovich space @@ -1572,6 +1594,9 @@ def join(self, other, basepoint="infty"): sage: Q7 = B(1/27,1/27) sage: Q1.join(Q7,Q2) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q1.join(Q2, Q7) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 """ parent = self.parent() @@ -1624,44 +1649,50 @@ def join(self, other, basepoint="infty"): b_greater_than_s = basepoint.partial_order(self) b_greater_than_o = basepoint.partial_order(other) + s_greater_than_o = self.partial_order(other) - if b_greater_than_s == None and b_greater_than_o == None: - dist_b_s = (self.center()[0] - basepoint.center()[0]).abs() - dist_b_o = (other.center()[0] - basepoint.center()[0]).abs() - return parent(basepoint.center(),\ - min(max(dist_b_o,other.radius(),basepoint.radius()),\ - max(dist_b_s,self.radius(),basepoint.radius()))) - - elif b_greater_than_s != None and b_greater_than_o == None: - dist_b_o = (other.center()[0] - basepoint.center()[0]).abs() - if dist_b_o < self.radius(): - return parent(basepoint.center(),dist_b_o) - elif dist_b_o >= self.radius(): - if b_greater_than_s: - return basepoint - else: - return self + #we deal with all the cases where self and other are not comparable first - elif b_greater_than_s == None and b_greater_than_o != None: - return other.join(self,basepoint) + if s_greater_than_o == None: + if b_greater_than_o == None: + if b_greater_than_s == None: + #case where none of the points are comparable + dist_b_s = (self.center()[0] - basepoint.center()[0]).abs() + dist_b_o = (other.center()[0] - basepoint.center()[0]).abs() + return parent(basepoint.center(),\ + min(max(dist_b_o,other.radius(),basepoint.radius()),\ + max(dist_b_s,self.radius(),basepoint.radius()))) - else: - if b_greater_than_s: - if b_greater_than_o: - if self.partial_order(other): - return self - else: - return other + #case where self and basepoint are comparable else: - return basepoint + if b_greater_than_s: + return basepoint + else: + return self + + #case where other and basepoint are comparable else: if b_greater_than_o: return basepoint else: - if self.partial_order(other): - return other - else: - return self + return other + + #now the cases where self > other + + elif s_greater_than_o: + if b_greater_than_s == None: + return self + if b_greater_than_s: + return self + if b_greater_than_o: + return basepoint + if b_greater_than_o == False: + return other + + #join is symmetric, so we flip self and other so that self > other + + else: + return other.join(self,basepoint) def involution_map(self): """ @@ -1739,8 +1770,8 @@ def potential_kernel(self, other, basepoint): INPUT: - - ``other`` - the point with which to take the potential kernel - - ``basepoint`` - the basepoint with which to take the potential kernel + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1773,7 +1804,7 @@ def spherical_kernel(self,other): INPUT: - - ``other`` - The point with which to take the spherical kernel + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number @@ -1805,8 +1836,8 @@ def Hsia_kernel(self, other, basepoint): INPUT:: - - ``other`` - the point with which to take the Hsia kernel - - ``basepoint`` - the basepoint with which to take the Hsia kernel + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1857,8 +1888,8 @@ def diameter(self, basepoint="oo"): INPUT: - - ``basepoint`` - (default = Infinity) the basepoint of - the generalized diameter + - ``basepoint`` -- (default = Infinity) A point of the same + Berkovich space as this point, or the string 'oo' OUTPUT: A real number or the infinity symbol 'oo' """ @@ -1918,7 +1949,6 @@ def __init__(self,base): base = Qp(base) #TODO chance to Qpbar else: raise ValueError("Non-prime pased into Berkovich space") - from sage.rings.padics.generic_nodes import is_pAdicField if not is_pAdicField(base): #TODO change base to Qpbar(prime) raise ValueError("Base of Berkovich Space must be a padic field") self._p = base.prime() @@ -1992,7 +2022,6 @@ def __init__(self,base): from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(base): raise ValueError("Base of Projective Berkovich Space must be Projective Space") - from sage.rings.padics.generic_nodes import is_pAdicField if not (is_pAdicField(base.base_ring())): raise ValueError("Base of Projective Berkovich Space must be " + \ "Projective Space over Qp") @@ -2016,7 +2045,14 @@ def _coerce_map_from_(self,S): if S.prime() == self.prime(): return True return False - + + def convex_hull(self, points): + """ + The convex hull of a set of points. + + The convex hull + """ + def _repr_(self): return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ self.base().base_ring().precision_cap()) From 9f294e20240c1a99b7c7594a1aec785ef605cfda Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 18 Jun 2020 20:47:30 -0400 Subject: [PATCH 010/379] 29844: Added __hash__, contained_in_interval, interval_intersection, and convex_hull functions --- src/sage/schemes/berkovich/berkovich_space.py | 162 ++++++++++++++++-- 1 file changed, 151 insertions(+), 11 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index fdbd5b0c691..085ef3f173a 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -29,7 +29,7 @@ class Berkovich_Element(Element): class Berkovich_Element_Cp(Berkovich_Element): """ - The parent class for any element of Berkovich space over ``Cp``. + The abstract parent class for any element of Berkovich space over ``Cp``. This class should never be instantiated, instead Berkovich_Element_Cp_Affine or Berkovich_Element_Cp_Projective should be used. """ @@ -484,6 +484,47 @@ def diameter(self): return radius_expr.limit(x="oo") return self._radius + def contained_in_interval(self, start, end): + """ + Checks if this point is an element of the interval [``start``,``end``]. + + INPUT: + + - ``start`` -- A point of the same Berkovich space as this point + - ``end`` -- A point of the same Berkovich space as this point + + OUTPUT: + + - ``True`` if this point is an element of [``start``,``end``] + - ``False`` otherwise + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: Q1 = B(2,1) + sage: Q2 = B(2,4) + sage: Q3 = B(1/3) + sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) + False + + :: + + sage: Q4 = B(1/81,1) + sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) + True + """ + if not isinstance(start, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if start.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + if not isinstance(end, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if end.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + join = start.join(end) + return join.partial_order(self) and (self.partial_order(start) \ + or self.partial_order(end)) + def hyperbolic_metric(self, other): """ The hyperbolic distance between this point and ``other``. @@ -1898,13 +1939,117 @@ def diameter(self, basepoint="oo"): else: return self.Hsia_kernel(self,basepoint) -class Berkovich(UniqueRepresentation,Parent): +class Berkovich(UniqueRepresentation, Parent): """ The parent class for any Berkovich space """ pass -class Berkovich_Cp_Affine(Berkovich): +class Berkovich_Cp(Berkovich): + """ + Abstract parent class for Berkovich space over ``Cp``. + """ + + def interval_intersection(self, I1, I2): + """ + The intersection of the two intervals ``I1`` and ``I2``. + + An 'interval' in Berkovich space is a tuple of the + form (start, end), where both start and end are + elements of this Berkovich space. The interval is defined + as the unique path from start to end. + + INPUT: + + - ``I1`` -- A list or tuple of length two, where the both elements + are points of this Berkovich space + - ``I2`` -- A list or tuple of length two, where the both elements + are points of this Berkovich space + + OUTPUT: An interval of Berkovich space which equals the intersection + of ``I1`` and ``I2``, or ``None`` if the intersection is empty. + """ + if not (isinstance(I1, list) or isinstance(I1,tuple)): + raise ValueError("I1 must be a tuple or a list") + if not (isinstance(I2, list) or isinstance(I2,tuple)): + raise ValueError("I2 must be a tuple or a list") + if len(I1) != 2: + raise ValueError("I1 must be length 2") + if len(I2) != 2: + raise ValueError("I2 must be length 2") + for j in [I1,I2]: + for i in j: + if not isinstance(i, Berkovich_Element_Cp): + raise ValueError("Intervals must start and end at points of Berkovich space") + if i.parent() != self: + raise ValueError("Intervals must start and end at points of this Berkovich space") + + start = I1[0].join(I1[1],I2[0]) + end = I1[0].join(I1[1],I2[0]) + if start.contained_in_interval(I2[0],I2[1]) and end.contained_in_interval(I2[0],I2[1]): + return (start, end) + return None + + def convex_hull(self, points): + """ + Returns the convex hull of a set of points. + + The convex hull of a set of points is the smallest + path-connected space that contains all the point, + or equivalently, the union of all intervals starting + and ending at points in the set. + + INPUT: + + - points -- A list of points of this Berkovich space. + + OUTPUT: A bipartite graph + """ + if not (isinstance(points, list) or isinstance(points, tuple)): + raise ValueError("input to convex_hull must be a list") + for i in points: + if not isinstance(i, Berkovich_Element_Cp): + raise ValueError("input to convex_hull must be a list of points of Berkovich space") + if i.parent() != self: + raise ValueError("input to convex_hull must be a list of points of this Berkovich space") + point_to_name = {} + for i in range(len(points)): + point_to_name[points[i]] = "P" + str(i) + V = points[:] + for i in range(len(points)): + for j in range(i+1,len(points)): + join = points[i].join(points[j]) + if join not in V: + V.append(join) + join_label = point_to_name[points[i]] + " ^ " + point_to_name[points[j]] + point_to_name[join] = join_label + E_first_pass = {} + for i in range(len(V)): + outgoing_first_pass = [] + for j in range(len(V)): + if j != i: + comparison = V[i].partial_order(V[j]) + if comparison: + #potential edge + outgoing_first_pass.append(V[j]) + E_first_pass[V[i]] = outgoing_first_pass + E_final = {} + for start in V: + outgoing = {} + for end in E_first_pass[start]: + good_end = True + for v in V: + if v != start and v != end: + contained = v.contained_in_interval(start,end) + if contained: + good_end = False + break + if good_end: + outgoing[point_to_name[end]] = 'replace' #TODO write good names for edges + E_final[point_to_name[start]] = outgoing + return E_final + +class Berkovich_Cp_Affine(Berkovich_Cp): r""" The Berkovich Affine line over `\CC_p`. @@ -1975,7 +2120,7 @@ def _latex_(self): Element = Berkovich_Element_Cp_Affine -class Berkovich_Cp_Projective(Berkovich): +class Berkovich_Cp_Projective(Berkovich_Cp): r""" The Berkovich Projective line over `\CC_p`. @@ -2016,6 +2161,8 @@ def __init__(self,base): if base in ZZ: if base.is_prime(): base = ProjectiveSpace(Qp(base),1) + Berkovich_Cp_Projective.__init__(self,base) + return else: raise ValueError("Non-prime pased into Berkovich space") else: @@ -2046,13 +2193,6 @@ def _coerce_map_from_(self,S): return True return False - def convex_hull(self, points): - """ - The convex hull of a set of points. - - The convex hull - """ - def _repr_(self): return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ self.base().base_ring().precision_cap()) From 030989e0fb6e1195daca29c67d2eefb100332f9f Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 19 Jun 2020 13:48:36 -0400 Subject: [PATCH 011/379] 29844: fixed error in contained_in_interval --- src/sage/schemes/berkovich/berkovich_space.py | 147 +++++++++++++----- 1 file changed, 104 insertions(+), 43 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 085ef3f173a..9c65ce5a2b2 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -484,47 +484,6 @@ def diameter(self): return radius_expr.limit(x="oo") return self._radius - def contained_in_interval(self, start, end): - """ - Checks if this point is an element of the interval [``start``,``end``]. - - INPUT: - - - ``start`` -- A point of the same Berkovich space as this point - - ``end`` -- A point of the same Berkovich space as this point - - OUTPUT: - - - ``True`` if this point is an element of [``start``,``end``] - - ``False`` otherwise - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective((3)) - sage: Q1 = B(2,1) - sage: Q2 = B(2,4) - sage: Q3 = B(1/3) - sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) - False - - :: - - sage: Q4 = B(1/81,1) - sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) - True - """ - if not isinstance(start, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") - if start.parent() != self.parent(): - raise ValueError("start must be a point of the same Berkovich space as this point") - if not isinstance(end, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") - if end.parent() != self.parent(): - raise ValueError("start must be a point of the same Berkovich space as this point") - join = start.join(end) - return join.partial_order(self) and (self.partial_order(start) \ - or self.partial_order(end)) - def hyperbolic_metric(self, other): """ The hyperbolic distance between this point and ``other``. @@ -1168,6 +1127,49 @@ def involution_map(self): new_radius_lst.append(new_radius) return parent(new_center_lst,new_radius_lst,error_check=False) + def contained_in_interval(self, start, end): + """ + Checks if this point is an element of the interval [``start``,``end``]. + + INPUT: + + - ``start`` -- A point of the same Berkovich space as this point + - ``end`` -- A point of the same Berkovich space as this point + + OUTPUT: + + - ``True`` if this point is an element of [``start``,``end``] + - ``False`` otherwise + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: Q1 = B(2,1) + sage: Q2 = B(2,4) + sage: Q3 = B(1/3) + sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) + False + + :: + + sage: Q4 = B(1/81,1) + sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) + True + """ + if not isinstance(start, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if start.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + if not isinstance(end, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if end.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + + #we treat infinity as a special case + base = self._base_space + P = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) + return P(self).contained_in_interval(P(start),P(end)) + def potential_kernel(self, other, basepoint): """ The potential kernel of this point with ``other``, with basepoint ``basepoint``. @@ -1804,6 +1806,64 @@ def involution_map(self): new_radius_lst.append(new_radius) return parent(new_center_lst,new_radius_lst) + def contained_in_interval(self, start, end): + """ + Checks if this point is an element of the interval [``start``,``end``]. + + INPUT: + + - ``start`` -- A point of the same Berkovich space as this point + - ``end`` -- A point of the same Berkovich space as this point + + OUTPUT: + + - ``True`` if this point is an element of [``start``,``end``] + - ``False`` otherwise + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: Q1 = B(2,1) + sage: Q2 = B(2,4) + sage: Q3 = B(1/3) + sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) + False + + :: + + sage: Q4 = B(1/81,1) + sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) + True + """ + if not isinstance(start, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if start.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + if not isinstance(end, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if end.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + + #we treat infinity as a special case + infty = (self.parent())((1,0)) + zero = (self.parent())(0) + if self == infty: + if start == zero or end == zero: + return end == infty or start == infty + return (self.involution_map()).contained_in_interval(start.involution_map(),\ + end.involution_map()) + if start == infty or end == infty: + if self == zero: + return end == zero or start == zero + if start == zero or end == zero: + gauss = (self.parent())(0,1) + return self.contained_in_interval(start,gauss) or self.contained_in_interval(gauss,end) + return (self.involution_map()).contained_in_interval(start.involution_map(),\ + end.involution_map()) + join = start.join(end) + return join.partial_order(self) and (self.partial_order(start) \ + or self.partial_order(end)) + def potential_kernel(self, other, basepoint): """ The potential kernel of this point with ``other``, @@ -2014,7 +2074,7 @@ def convex_hull(self, points): raise ValueError("input to convex_hull must be a list of points of this Berkovich space") point_to_name = {} for i in range(len(points)): - point_to_name[points[i]] = "P" + str(i) + point_to_name[points[i]] = "P" + str(i+1) V = points[:] for i in range(len(points)): for j in range(i+1,len(points)): @@ -2047,7 +2107,8 @@ def convex_hull(self, points): if good_end: outgoing[point_to_name[end]] = 'replace' #TODO write good names for edges E_final[point_to_name[start]] = outgoing - return E_final + from sage.graphs.digraph import DiGraph + return DiGraph(E_final) class Berkovich_Cp_Affine(Berkovich_Cp): r""" From 23cb92f1e591af7eeb65b0cfefafe532cd0a0441 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 19 Jun 2020 19:14:04 -0400 Subject: [PATCH 012/379] 29844: Fixed reference, removed convex_hull --- src/doc/en/reference/schemes/index.rst | 7 + src/sage/schemes/berkovich/berkovich_space.py | 520 ++++++++++-------- 2 files changed, 297 insertions(+), 230 deletions(-) diff --git a/src/doc/en/reference/schemes/index.rst b/src/doc/en/reference/schemes/index.rst index a672067914d..1f4a64f0c7c 100644 --- a/src/doc/en/reference/schemes/index.rst +++ b/src/doc/en/reference/schemes/index.rst @@ -93,4 +93,11 @@ Cyclic Covers sage/schemes/cyclic_covers/charpoly_frobenius sage/schemes/cyclic_covers/constructor +Berkovich Analytic Space +------------------------ +.. toctree:: + :maxdepth: 1 + + sage/schemes/berkovich/berkovich_space + .. include:: ../footer.txt diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 9c65ce5a2b2..436a35f3d2b 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -1,5 +1,5 @@ -""" -A framework for implementing the Berkovich construction over a scheme. +r""" +Berkovich Space over `\CC_p` """ from sage.structure.parent import Parent @@ -28,8 +28,8 @@ class Berkovich_Element(Element): pass class Berkovich_Element_Cp(Berkovich_Element): - """ - The abstract parent class for any element of Berkovich space over ``Cp``. + r""" + The abstract parent class for any element of Berkovich space over `\CC_p`. This class should never be instantiated, instead Berkovich_Element_Cp_Affine or Berkovich_Element_Cp_Projective should be used. """ @@ -249,7 +249,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, flag = True if flag: raise ValueError("The center of a point of Projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s") %self._base_space + "point of P^1(Cp) or coerce into %s" %self._base_space) elif not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: @@ -615,6 +615,21 @@ def Hsia_kernel_infinity(self, other): def center(self): """ Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. + + OUTPUT: An element of the ``base_ring`` of the parent Berkovich + Space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(3,1).center() + 3 + O(3^21) + + :: + + sage: C = Berkovich_Cp_Projective(3) + sage: C(3,1).center() + (3 + O(3^21) : 1 + O(3^20)) """ if self._type == 4: return self._center_lst[:] @@ -625,6 +640,17 @@ def type_of_point(self): Returns the Type of this point of Berkovich space over ``Cp`` OUTPUT: An integer between 1 and 4 inclusive + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(1).type_of_point() + 1 + + :: + + sage: B(0,1).type_of_point() + 2 """ return self._type @@ -633,6 +659,12 @@ def prime(self): Shorthand for residue characteristic of the parent OUTPUT: A prime integer + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(1).prime() + 3 """ return self._p @@ -643,6 +675,9 @@ def __ne__(self, other): return not (self == other) def _repr_(self): + """ + String representation of this point. + """ if self._type == 1: return "Type I point centered at " + format(self._center) elif self._type == 2: @@ -664,6 +699,9 @@ def _repr_(self): %(self._center_lst[:2],self._radius_lst[:2]) def _latex_(self): + """ + LaTeX representation of this point. + """ from sage.misc.latex import latex if self._type == 1: text = r"the point %s of } \Bold{C}_%s" %(self._center, self._p) @@ -691,37 +729,37 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): Elements are categorized into four Types, represented by specific data: - - Type I points are represented by a center in `\QQ_p` or a finite extension + - Type I points are represented by a center in `\QQ_p` or a finite extension - - Type II points are represented by a center in `\QQ_p` and a rational power of `p` + - Type II points are represented by a center in `\QQ_p` and a rational power of `p` - - Type III points are represented by a center in `\QQ_p` and a radius in `[0,\infty)` + - Type III points are represented by a center in `\QQ_p` and a radius in `[0,\infty)` - - Type IV points are represented by a finite list of centers in `\QQ_p` and - a finite list of radii in `[0,\infty)`. + - Type IV points are represented by a finite list of centers in `\QQ_p` and + a finite list of radii in `[0,\infty)`. INPUT: - ``center`` -- For Type I, II, and III points, the center of the - corresponding disk in `\CC_p`. Must be an element of `\QQ_p`, a finite extension - of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers - used to approximate the point or a univariate function that computes the centers - (computation starts at 1). + corresponding disk in `\CC_p`. Must be an element of `\QQ_p`, a finite extension + of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers + used to approximate the point or a univariate function that computes the centers + (computation starts at 1). - ``radius`` -- (optional) For Type I, II, and III points, the radius of the - corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, - can be a list of radii used to approximate the point or a univariate function that - computes the radii (computation starts at 1). + corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, + can be a list of radii used to approximate the point or a univariate function that + computes the radii (computation starts at 1). - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies - the power of ``p`` such that p^power = radius + the power of ``p`` such that p^ ``power`` = radius - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point - ``error_check`` -- (default: True) If error checking should be run on input. If - input is correctly formatted, can be set to ``False`` for better performance. - WARNING: with error check set to ``False``, any error in the input will lead to - incorrect results. + input is correctly formatted, can be set to ``False`` for better performance. + WARNING: with error check set to ``False``, any error in the input will lead to + incorrect results. EXAMPLES: @@ -874,8 +912,8 @@ def center(self): OUTPUT: - - For Type I-III points, a point of ``Cp`` - - For Type IV points, a list of points of ``Cp`` + - For Type I-III points, a point of ``Cp`` + - For Type IV points, a list of points of ``Cp`` EXAMPLES:: @@ -936,13 +974,13 @@ def partial_order(self,other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: - - ``True`` - If self > other in the standard partial order - - ``False`` - If self < other in the standard partial order - - ``None`` - If the two points are not comparable + - ``True`` -- If self > other in the standard partial order + - ``False`` -- If self < other in the standard partial order + - ``None`` -- If the two points are not comparable EXAMPLES:: @@ -952,19 +990,19 @@ def partial_order(self,other): sage: Q1.partial_order(Q2) False - :: + :: sage: Q3 = B(1/2) sage: Q1.partial_order(Q3) True - :: + :: sage: Q4 = B(1/81,1) sage: print(Q4.partial_order(Q1)) None - :: + :: sage: print(Q4.partial_order(Q3)) None @@ -992,9 +1030,9 @@ def join(self, other, basepoint="infty"): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point or the string 'infty' + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- (default: Infinity) A point of the same + Berkovich space as this point or the string 'infty' OUTPUT: A point of the same Berkovich space @@ -1062,7 +1100,7 @@ def involution_map(self): """ Returns the image of this point under the involution map. - The involution map is the extension of the map z |-> 1/z map + The involution map is the extension of the map ``z |-> 1/z`` map on ``Cp`` to Berkovich space. For Affine Berkovich Space, not defined for the Type I @@ -1070,7 +1108,7 @@ def involution_map(self): OUTPUT: A point of the same Berkovich space - EXAMPLES:: + EXAMPLES: The involution map is 1/z on Type I points:: @@ -1133,13 +1171,13 @@ def contained_in_interval(self, start, end): INPUT: - - ``start`` -- A point of the same Berkovich space as this point - - ``end`` -- A point of the same Berkovich space as this point + - ``start`` -- A point of the same Berkovich space as this point + - ``end`` -- A point of the same Berkovich space as this point OUTPUT: - - ``True`` if this point is an element of [``start``,``end``] - - ``False`` otherwise + - ``True`` if this point is an element of [``start``,``end``] + - ``False`` otherwise EXAMPLES:: @@ -1150,7 +1188,7 @@ def contained_in_interval(self, start, end): sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) False - :: + :: sage: Q4 = B(1/81,1) sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) @@ -1176,8 +1214,8 @@ def potential_kernel(self, other, basepoint): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1205,11 +1243,12 @@ def potential_kernel(self, other, basepoint): return basepoint.path_distance_metric((self.join(other,basepoint))) def spherical_kernel(self,other): - """ + r""" The spherical kernel of this point with ``other``. The spherical kernel is one possible extension of - the spherical distance on P^1(``Cp``) to P^1 Berkovich. + the spherical distance on `A^1(\CC_p)` to the Berkovich + Affine line. OUTPUT: A real number @@ -1240,8 +1279,8 @@ def Hsia_kernel(self, other, basepoint): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1254,7 +1293,7 @@ def Hsia_kernel(self, other, basepoint): sage: Q1.Hsia_kernel(Q2,Q3) 0.111111111111111 - :: + :: sage: B = Berkovich_Cp_Affine(Qp(3)) sage: Q1 = B(2,9) @@ -1292,10 +1331,23 @@ def diameter(self, basepoint="oo"): INPUT: - - ``basepoint`` -- (default = Infinity) A point of the - same Berkovich space as this point + - ``basepoint`` -- (default = Infinity) A point of the + same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1/81,1) + sage: Q2 = B(1/3) + sage: Q1.diameter(Q2) + 0.00137174211248285 + + :: + + sage: Q2.diameter(Q2) + 'oo' """ if basepoint == "oo": return super().diameter() @@ -1304,48 +1356,48 @@ def diameter(self, basepoint="oo"): class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" - Element class of the Berkovich Projective line over `\CC_p` - + Element class of the Berkovich Projective line over `\CC_p`. + Elements are categorized into four Types, which are represented as follows: - - Type I points are represented by a center in Projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` + - Type I points are represented by a center in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p`. - - Type II points are represented by a center in Projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` and a rational power of `p`. - Type II points cannot be centered at the point at infinity. + - Type II points are represented by a center in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` and a rational power of `p`. + Type II points cannot be centered at the point at infinity. - - Type III points are represented by a center in Projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` and a radius in [0,`\infty`). - Type III points are cannot be centered at the point at infinity. + - Type III points are represented by a center in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` and a radius in `[0,\infty)`. + Type III points are cannot be centered at the point at infinity. - - Type IV points are represented by finite list of centers in Projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` and by a finite list of radii in [0,`\infty`). - None of the centers can be the point at infinity. + - Type IV points are represented by finite list of centers in Projective Space of dimension 1 over + `\QQ_p` or over a finite extension of `\QQ_p` and by a finite list of radii in `[0,\infty)`. + None of the centers can be the point at infinity. INPUT: - ``center`` -- For Type I, II, and III points, the center of the - corresponding disk in `P^1(\CC_p)`. Must be an element of `\QQ_p`, a finite extension - of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers - used to approximate the point or a univariate function that computes the centers - (computation starts at 1). + corresponding disk in `P^1(\CC_p)`. Must be an element of `\QQ_p`, a finite extension + of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers + used to approximate the point or a univariate function that computes the centers + (computation starts at 1). - ``radius`` -- (optional) For Type I, II, and III points, the radius of the - corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, - can be a list of radii used to approximate the point or a univariate function that - computes the radii (computation starts at 1). + corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, + can be a list of radii used to approximate the point or a univariate function that + computes the radii (computation starts at 1). - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies - the power of ``p`` such that p^power = radius + the power of ``p`` such that p^ ``power`` = radius. - - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point + - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point. - ``error_check`` -- (default: True) If error checking should be run on input. If - input is correctly formatted, can be set to ``False`` for better performance. - WARNING: Setting error_check to ``False`` can lead to incorrect results. + input is correctly formatted, can be set to ``False`` for better performance. + WARNING: Setting error_check to ``False`` can lead to incorrect results. - EXAMPLES:: + EXAMPLES: Type I points can be created by specifying the corresponding point of `P^1(\CC_p)`:: @@ -1439,13 +1491,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check prec=prec,child="projective",error_check=error_check) def center(self): - """ - Returns the center of the corresponding disk (or sequence of disks) in P^1(Cp) + r""" + Returns the center of the corresponding disk (or sequence of disks) in `P^1(\CC_p)`. OUTPUT: - - For Type I-III points, a point of P^1(Cp) - - For Type IV points, a list of points of P^1(Cp) + - For Type I-III points, a point of `P^1(\CC_p)` + - For Type IV points, a list of points of `P^1(\CC_p)` EXAMPLES:: @@ -1507,12 +1559,14 @@ def partial_order(self,other): is a subset of D(c2,r2) in ``Cp``. INPUT: - - ``other`` -- A point of the same Berkovich space as this point + + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: - - ``True`` - If self > other in the standard partial order - - ``False`` - If other > self in the standard partial order - - ``None`` - If the two points are not comparable + + - ``True`` - If self > other in the standard partial order + - ``False`` - If other > self in the standard partial order + - ``None`` - If the two points are not comparable EXAMPLES:: @@ -1601,10 +1655,10 @@ def join(self, other, basepoint="infty"): basepoint. INPUT: - - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point or the string 'infty' + + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- (default: Infinity) A point of the same + Berkovich space as this point or the string 'infty' OUTPUT: A point of the same Berkovich space @@ -1741,12 +1795,12 @@ def involution_map(self): """ Returns the image of this point under the involution map. - The involution map is the extension of the map z |-> 1/z map + The involution map is the extension of the map ``z |-> 1/z`` map on projective space over ``Cp`` to Berkovich space. OUTPUT: A point of the same Berkovich space - EXAMPLES:: + EXAMPLES: The involution map is 1/z on Type I points:: @@ -1808,17 +1862,17 @@ def involution_map(self): def contained_in_interval(self, start, end): """ - Checks if this point is an element of the interval [``start``,``end``]. + Checks if this point is an element of the interval [``start``, ``end``]. INPUT: - - ``start`` -- A point of the same Berkovich space as this point - - ``end`` -- A point of the same Berkovich space as this point + - ``start`` -- A point of the same Berkovich space as this point + - ``end`` -- A point of the same Berkovich space as this point OUTPUT: - - ``True`` if this point is an element of [``start``,``end``] - - ``False`` otherwise + - ``True`` if this point is an element of [``start``, ``end``] + - ``False`` otherwise EXAMPLES:: @@ -1829,7 +1883,7 @@ def contained_in_interval(self, start, end): sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) False - :: + :: sage: Q4 = B(1/81,1) sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) @@ -1871,8 +1925,8 @@ def potential_kernel(self, other, basepoint): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1897,15 +1951,15 @@ def potential_kernel(self, other, basepoint): return basepoint.path_distance_metric((self.join(other,basepoint))) def spherical_kernel(self,other): - """ + r""" The spherical kernel of this point with ``other``. The spherical kernel is one possible extension of the spherical - distance on P^1(``Cp``) to P^1 Berkovich. + distance on `P^1(\CC_p)` to the Projective Berkovich line. INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number @@ -1935,10 +1989,10 @@ def Hsia_kernel(self, other, basepoint): The Hsia kernel of this point and ``other``, with basepoint ``basepoint``. - INPUT:: + INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point OUTPUT: A real number or the infinity symbol 'oo' @@ -1989,10 +2043,23 @@ def diameter(self, basepoint="oo"): INPUT: - - ``basepoint`` -- (default = Infinity) A point of the same - Berkovich space as this point, or the string 'oo' + - ``basepoint`` -- (default = Infinity) A point of the same + Berkovich space as this point, or the string 'oo' OUTPUT: A real number or the infinity symbol 'oo' + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(1/81,1) + sage: Q2 = B(1/3) + sage: Q1.diameter(Q2) + 0.00137174211248285 + + :: + + sage: Q2.diameter(Q2) + 'oo' """ if basepoint == "oo": return super().diameter() @@ -2010,125 +2077,34 @@ class Berkovich_Cp(Berkovich): Abstract parent class for Berkovich space over ``Cp``. """ - def interval_intersection(self, I1, I2): - """ - The intersection of the two intervals ``I1`` and ``I2``. - - An 'interval' in Berkovich space is a tuple of the - form (start, end), where both start and end are - elements of this Berkovich space. The interval is defined - as the unique path from start to end. - - INPUT: - - - ``I1`` -- A list or tuple of length two, where the both elements - are points of this Berkovich space - - ``I2`` -- A list or tuple of length two, where the both elements - are points of this Berkovich space - - OUTPUT: An interval of Berkovich space which equals the intersection - of ``I1`` and ``I2``, or ``None`` if the intersection is empty. - """ - if not (isinstance(I1, list) or isinstance(I1,tuple)): - raise ValueError("I1 must be a tuple or a list") - if not (isinstance(I2, list) or isinstance(I2,tuple)): - raise ValueError("I2 must be a tuple or a list") - if len(I1) != 2: - raise ValueError("I1 must be length 2") - if len(I2) != 2: - raise ValueError("I2 must be length 2") - for j in [I1,I2]: - for i in j: - if not isinstance(i, Berkovich_Element_Cp): - raise ValueError("Intervals must start and end at points of Berkovich space") - if i.parent() != self: - raise ValueError("Intervals must start and end at points of this Berkovich space") - - start = I1[0].join(I1[1],I2[0]) - end = I1[0].join(I1[1],I2[0]) - if start.contained_in_interval(I2[0],I2[1]) and end.contained_in_interval(I2[0],I2[1]): - return (start, end) - return None - - def convex_hull(self, points): - """ - Returns the convex hull of a set of points. - - The convex hull of a set of points is the smallest - path-connected space that contains all the point, - or equivalently, the union of all intervals starting - and ending at points in the set. - - INPUT: + def __eq__(self,right): + + if not isinstance(right, Berkovich_Cp): + return False + return self.base() == right.base() - - points -- A list of points of this Berkovich space. - - OUTPUT: A bipartite graph - """ - if not (isinstance(points, list) or isinstance(points, tuple)): - raise ValueError("input to convex_hull must be a list") - for i in points: - if not isinstance(i, Berkovich_Element_Cp): - raise ValueError("input to convex_hull must be a list of points of Berkovich space") - if i.parent() != self: - raise ValueError("input to convex_hull must be a list of points of this Berkovich space") - point_to_name = {} - for i in range(len(points)): - point_to_name[points[i]] = "P" + str(i+1) - V = points[:] - for i in range(len(points)): - for j in range(i+1,len(points)): - join = points[i].join(points[j]) - if join not in V: - V.append(join) - join_label = point_to_name[points[i]] + " ^ " + point_to_name[points[j]] - point_to_name[join] = join_label - E_first_pass = {} - for i in range(len(V)): - outgoing_first_pass = [] - for j in range(len(V)): - if j != i: - comparison = V[i].partial_order(V[j]) - if comparison: - #potential edge - outgoing_first_pass.append(V[j]) - E_first_pass[V[i]] = outgoing_first_pass - E_final = {} - for start in V: - outgoing = {} - for end in E_first_pass[start]: - good_end = True - for v in V: - if v != start and v != end: - contained = v.contained_in_interval(start,end) - if contained: - good_end = False - break - if good_end: - outgoing[point_to_name[end]] = 'replace' #TODO write good names for edges - E_final[point_to_name[start]] = outgoing - from sage.graphs.digraph import DiGraph - return DiGraph(E_final) + def __neq__(self,right): + return not self == right class Berkovich_Cp_Affine(Berkovich_Cp): r""" The Berkovich Affine line over `\CC_p`. - The Berkovich Affine line can be thought of as the set of seminorms on `\CC_p[x]`, + The Berkovich Affine line is the set of seminorms on `\CC_p[x]`, with the weakest topology that makes the map `| \cdot | \to |f|` continuous for all `f \in \CC_p[x]`. INPUT: - - ``base`` - The prime ``p``. Alternative, can be `\QQ_p` or a finite extension. - This allows for more control over automated conversion of centers of points. + - ``base`` - The prime ``p``. Alternative, can be `\QQ_p` or a finite extension. + This allows for more control over automated conversion of centers of points. EXAMPLES:: sage: B = Berkovich_Cp_Affine(3); B Affine Berkovich line over Cp(3) of precision 20 - Initializing by passing in Qp space looks the same:: + Initializing by passing in ``Qp`` looks the same:: sage: B = Berkovich_Cp_Affine(Qp(3)); B Affine Berkovich line over Cp(3) of precision 20 @@ -2148,6 +2124,9 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: B = Berkovich_Cp_Affine(Qp(3,1000)); B Affine Berkovich line over Cp(3) of precision 1000 """ + + Element = Berkovich_Element_Cp_Affine + def __init__(self,base): from sage.rings.integer_ring import ZZ if base in ZZ: @@ -2158,40 +2137,69 @@ def __init__(self,base): if not is_pAdicField(base): #TODO change base to Qpbar(prime) raise ValueError("Base of Berkovich Space must be a padic field") self._p = base.prime() - Parent.__init__(self, base = base, category=TopologicalSpaces()) + Parent.__init__(self, base = base, category=TopologicalSpaces()) def residue_characteristic(self): + """ + The residue characteristic of the ``base``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B.residue_characteristic() + 3 + """ return self._p def prime(self): - return self._p + """ + Short hand for ``residue_characteristic``. - def _coerce_map_from_(self,S): - if isinstance(S, Berkovich_Cp_Affine): - if S.prime() == self.prime(): - return True - return False + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B.prime() + 3 + """ + return self._p def _repr_(self): + """ + String representation of this Berkovich Space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B + Affine Berkovich line over Cp(3) of precision 20 + """ return "Affine Berkovich line over Cp(%s) of precision %s" \ %(self.prime(),self.base().precision_cap()) def _latex_(self): - return r"\text{Affine Berkovich line over } \Bold{C}_{%s}" %(self.prime()) + r""" + LaTeX representation of this Berkovich Space. - Element = Berkovich_Element_Cp_Affine + EXAMPLES: + + sage: B = Berkovich_Cp_Affine(3) + sage: latex(B) + \text{Affine Berkovich line over } \Bold{C}_{3} + """ + return r"\text{Affine Berkovich line over } \Bold{C}_{%s}" %(self.prime()) class Berkovich_Cp_Projective(Berkovich_Cp): r""" The Berkovich Projective line over `\CC_p`. - The Berkovich Projective line can be thought of as the one-point compactification + The Berkovich Projective line is the one-point compactification of the Berkovich Affine line. INPUT: - - base - The prime number `p`. Alternatively, can be a Projective Space over - `\QQ_p` or a finite extension of `\QQ_p`. This allows for more control of conversion of centers. + - ``base`` - The prime number `p`. Alternatively, can be a Projective Space over + `\QQ_p` or a finite extension of `\QQ_p`. This allows for more control of + conversion of centers. EXAMPLES:: @@ -2204,7 +2212,8 @@ class Berkovich_Cp_Projective(Berkovich_Cp): sage: B = Berkovich_Cp_Projective(S); B Projective Berkovich line over Cp(3) of precision 20 - However, this method allows for more control over behind-the-scenes conversion:: + However, this method allows for more control over + behind-the-scenes conversion:: sage: S = ProjectiveSpace(Qp(3,1),1) sage: B = Berkovich_Cp_Projective(S); B @@ -2214,9 +2223,11 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Type I point centered at (2 + O(3) : 1 + O(3)) Note that this point has very low precision, as S is a scheme over - Qp(3) of capped-relative precision one. - + `\QQ_3` of capped-relative precision 1. """ + + Element = Berkovich_Element_Cp_Projective + def __init__(self,base): from sage.rings.integer_ring import ZZ if base in ZZ: @@ -2243,24 +2254,73 @@ def __init__(self,base): Parent.__init__(self, base = base, category=TopologicalSpaces()) def residue_characteristic(self): + """ + The residue characteristic of the ``base``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B.residue_characteristic() + 3 + """ return self._p def prime(self): + """ + Short hand for ``residue_characteristic``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B.prime() + 3 + """ return self._p - - def _coerce_map_from_(self,S): - if isinstance(S, Berkovich_Cp_Affine) or isinstance(S,Berkovich_Cp_Projective): - if S.prime() == self.prime(): - return True - return False + + def base_ring(self): + r""" + The base of this Berkovich Space. + + OUTPUT: A Projective Space of dimension 1 over `\QQ_p` + or a finite extension. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B.base_ring() + Projective Space of dimension 1 over 3-adic Field with + capped relative precision 20 + + :: + + sage: C = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3,1),1)) + sage: C.base_ring() + Projective Space of dimension 1 over 3-adic Field with + capped relative precision 1 + """ + return self.base() def _repr_(self): + """ + String representation of this Berkovich Space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B + Projective Berkovich line over Cp(3) of precision 20 + """ return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ self.base().base_ring().precision_cap()) def _latex_(self): - return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) + r""" + LaTeX representation of this Berkovich Space. - Element = Berkovich_Element_Cp_Projective + EXAMPLES: - + sage: B = Berkovich_Cp_Projective(3) + sage: latex(B) + \text{Projective Berkovich line over } \Bold{C}_{3} + """ + return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) From dc3b587062ff796f45d322fc7948b07ef9e028bb Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 22 Jun 2020 15:06:40 -0400 Subject: [PATCH 013/379] Initial commit --- .../arithmetic_dynamics/berkovich_ds.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py new file mode 100644 index 00000000000..7925c709bf3 --- /dev/null +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -0,0 +1,67 @@ +r""" +Dynamical systmes on Berkovich space over \CC_p. + +A dynamical system on Berkovich space over \CC_p is +determined by a dynamical system on A^1(\CC_p) or P^1(\CC_p), +which naturally induces a dynamical system on affine or +projective Berkovich space. +""" + +from sage.structure.element import Element +from sage.dynamics.arithmetic_dynamics.generic_ds import DynamicalSystem +from sage.misc.classcall_metaclass import typecall +from sage.schemes.generic.morphism import SchemeMorphism_polynomial +from sage.rings.padics.generic_nodes import is_pAdicField +from sage.schemes.berkovich.berkovich_space import Berkovich_Cp_Affine, Berkovich_Cp_Projective + +class DynamicalSystem_Berkovich(Element): + + @staticmethod + def __classcall_private__(cls, system_morphism_polys, domain=None): + r""" + Base class for dynamical systems on Berkovich space over \CC_p. + + INPUT:: + + - ``system_morphism_polys`` -- a list of polynomials or rational functions, + or a dynamical system. In any case, this input should be + defined over affine or projective space of a finite extension of \QQ_p. + + - ``domain`` -- (optional) affine or projective Berkovich space over \CC_p + """ + if isinstance(system_morphism_polys,(list,tuple)): + polys = list(system_morphism_polys) + if len(system_morphism_polys) not in [1,2]: + raise ValueError('system_morphism_polys must be length 1 or 2') + from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + test = lambda x: is_PolynomialRing(x) or is_MPolynomialRing(x) + if not all(test(poly.parent()) for poly in polys): + try: + polys = [poly.lift() for poly in polys] + except AttributeError: + raise ValueError('{} must be elements of a polynomial ring'.format(system_morphism_polys)) + + if isinstance(system_morphism_polys, DynamicalSystem): + system_morphism_polys = system_morphism_polys.as_scheme_morphism() + + if isinstance(system_morphism_polys, SchemeMorphism_polynomial): + R = system_morphism_polys.base_ring() + morphism_domain = system_morphism_polys.domain() + polys = list(system_morphism_polys) + if morphism_domain != system_morphism_polys.codomain(): + raise ValueError('domain and codomain do not agree') + if not is_pAdicField(R): + raise ValueError('system_morphism_polys not defined over padic field') + if morphism_domain != morphism_domain.ambient_space(): + raise ValueError('morphism must be defined on the ambient space') + if domain == None: + domain = Berkovich_Cp_Projective(morphism_domain) + else: + from sage.schemes.berkovich.berkovich_space import Berkovich_Cp + if not isinstance(domain, Berkovich_Cp): + raise ValueError('domain must be Berkovich space over Cp') + return typecall(cls,polys,domain) + + + From e0079d30d275fae564ebd0b837df94fe06f33b54 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 22 Jun 2020 18:21:35 -0400 Subject: [PATCH 014/379] 29844: Added more examples + documentation --- src/sage/schemes/berkovich/berkovich_space.py | 316 ++++++++++++------ 1 file changed, 208 insertions(+), 108 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 436a35f3d2b..3964583ef0d 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -1,7 +1,34 @@ r""" Berkovich Space over `\CC_p` + +The Berkovich affine line is the set of seminorms on `\CC_p[x]`, +with the weakest topology that makes the map `| \cdot | \to |f|` continuous +for all `f \in \CC_p[x]`. The Berkovich projective line is the +one-point compactification of the Berkovich affine line. + +The two main classes are :class:`Berkovich_Cp_Affine` and +:class:`Berkovich_Cp_Projective`, which implement the affine and +projective lines, respectively. + +:class:`Berkovich_Cp_Affine` takes as input a prime `p` or a finite +extension of `\QQ_p`, while :class:`Berkovich_Cp_Projective` takes +as input a prime `p` or projective space of dimension 1 over a +finite extension of `\QQ_p`. + +AUTHORS: + + - Alexander Galarraga (2020-06-22): initial implementation + """ +#***************************************************************************** +# 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.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import Element @@ -18,8 +45,7 @@ from sage.schemes.affine.affine_space import AffineSpace from sage.schemes.generic.scheme import Scheme from sage.rings.rational_field import QQ - - +from sage.rings.infinity import Infinity class Berkovich_Element(Element): """ @@ -40,13 +66,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, from sage.rings.fraction_field_element import FractionFieldElement_1poly_field self._type = None - #if center is a list, this is a Type 4 point - if isinstance(center, list): + #if radius is a list, this is a Type 4 point + if isinstance(radius, list): if error_check: - if not isinstance(radius, list): + if not isinstance(center, list): raise ValueError("center was passed a list but radius was not a list") if len(radius) != len(center): - raise ValueError("The same number of centers and radii must be specified to create " + \ + raise ValueError("the same number of centers and radii must be specified to create " + \ "a Type IV point") self._center_lst = list(center) self._radius_lst = list(radius) @@ -68,7 +94,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if error_check: if is_Expression(center): if len(center.variables()) != 1: - raise ValueError("An expression with %s " %(len(center.variables())) + \ + raise ValueError("an expression with %s " %(len(center.variables())) + \ "variables cannot define the centers approximating a Type IV point") else: #we do this since .subs is currently bugged for polynomials but not expressions @@ -78,7 +104,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("center was passed a function but radius was not a function") if is_Expression(radius): if len(radius.variables()) != 1: - raise ValueError("An expression with %s " %(len(radius.variables())) + \ + raise ValueError("an expression with %s " %(len(radius.variables())) + \ "variables cannot define the radii approximating a Type IV point") else: radius_expr_check = True @@ -128,7 +154,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("The center of a point of Projective Berkovich" + \ + raise ValueError("the center of a point of Projective Berkovich" + \ "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) elif not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(),pAdicGeneric): @@ -138,7 +164,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("The center of a point of Projective Berkovich space must be a " + \ + raise ValueError("the center of a point of Projective Berkovich space must be a " + \ "point of P^1(Cp) or coerce into %s") %self._base_space else: try: @@ -146,13 +172,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: pass if center.scheme().ambient_space() != center.scheme(): - raise ValueError("The center of a point of Berkovich space over " + \ + raise ValueError("the center of a point of Berkovich space over " + \ "P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("The center of a disk approximating a Type IV point of Berkovich space" + \ + raise ValueError("the center of a disk approximating a Type IV point of Berkovich space" + \ " over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) if center == (center.scheme())((1,0)): - raise ValueError("The center of a disk approximating a Type IV point of Berkovich " + \ + raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ "space cannot be centered at %s" %((center.scheme())((1,0)))) #make sure the radius coerces into the reals if not is_RealNumber(radius): @@ -162,7 +188,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("The radius of a disk approximating a Type IV point" + \ + raise ValueError("the radius of a disk approximating a Type IV point" + \ "must coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks @@ -170,7 +196,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, previous_radius = self._radius_lst[i-1] dist = (center[0] - previous_center[0]).abs() if previous_radius < radius or dist > radius: - raise ValueError("Sequence of disks does not define a Type IV point as" + \ + raise ValueError("sequence of disks does not define a Type IV point as" + \ "containment is not proper") return elif child == "affine": @@ -186,7 +212,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("The center of a disk approximating a Type IV point must " + \ + raise ValueError("the center of a disk approximating a Type IV point must " + \ "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) elif not is_pAdicField(center.parent()): try: @@ -194,7 +220,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: pass if (center.parent()).prime() != self._p: - raise ValueError("The center of a disk approximating a Type IV point of Berkovich " + \ + raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ "space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) #make sure the radius coerces into the reals if not is_RealNumber(radius): @@ -204,7 +230,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("The radius of a disk approximating a Type IV point must " + \ + raise ValueError("the radius of a disk approximating a Type IV point must " + \ "coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks @@ -212,7 +238,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, previous_radius = self._radius_lst[i-1] dist = (center - previous_center).abs() if previous_radius < radius or dist > radius: - raise ValueError("Sequence of disks does not define a Type IV point as " + \ + raise ValueError("sequence of disks does not define a Type IV point as " + \ "containment is not proper") return else: @@ -230,7 +256,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("The center of a point of Affine Berkovich space over " + \ + raise ValueError("the center of a point of Affine Berkovich space over " + \ "%s must convert to %s" %(self._base_space,self._base_space)) elif not is_pAdicField(center.parent()): try: @@ -238,7 +264,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: pass if (center.parent()).prime() != self._p: - raise ValueError("The center of a point of Berkovich space over " + \ + raise ValueError("the center of a point of Berkovich space over " + \ "Cp(%s) cannot be a point of %s" %(self._p, center.parent())) elif child == "projective": if not isinstance(center, SchemeMorphism_point_projective_field): @@ -248,7 +274,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("The center of a point of Projective Berkovich space must be a " + \ + raise ValueError("the center of a point of Projective Berkovich space must be a " + \ "point of P^1(Cp) or coerce into %s" %self._base_space) elif not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): @@ -258,7 +284,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("The center of a point of Projective Berkovich space must be a " + \ + raise ValueError("the center of a point of Projective Berkovich space must be a " + \ "point of P^1(Cp) or coerce into %s") %self._base_space else: try: @@ -266,10 +292,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: pass if not(center.scheme().ambient_space() is center.scheme()): - raise ValueError("The center of a point of Projective Berkovich space cannot be " + \ + raise ValueError("the center of a point of Projective Berkovich space cannot be " + \ "a point of %s" %(center.scheme())) if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("The center of a point of Berkovich space over " + \ + raise ValueError("the center of a point of Berkovich space over " + \ "P^1(Cp(%s)) cannot be a point of %s" %(self._p, center.scheme())) else: raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ @@ -288,7 +314,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except ValueError: flag = True if flag: - raise ValueError("Type II and III points cannot be centered at (1 : 0)") + raise ValueError("type II and III points cannot be centered at (1 : 0)") if power != None: if error_check: if power.parent() != QQ: @@ -301,7 +327,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("power must convert to rationals") if radius != None: if radius != self._p**power: - raise ValueError("Conflicting inputs for power and radius") + raise ValueError("conflicting inputs for power and radius") self._power = power self._radius = RR(self._p**power) self._type = 2 @@ -319,13 +345,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("Symbolic radius must be a real number") + raise ValueError("symbolic radius must be a real number") from sage.rings.real_mpfr import is_RealNumber if (not is_RealNumber(radius)) and power == None: if RR.has_coerce_map_from(radius.parent()): self._radius = RR(radius) else: - raise ValueError("Radius must coerce into real numbers") + raise ValueError("radius must coerce into real numbers") else: self._radius = radius if power != None: @@ -385,8 +411,8 @@ def power(self): OUTPUT: - - An integer for Type II points - - A real number for Type III points + - An integer for Type II points + - A real number for Type III points EXAMPLES:: @@ -411,8 +437,8 @@ def radius(self): OUTPUT: - - A non-negative real number for points Types I-III - - A list of non-negative real numbers for Type IV points + - A non-negative real number for points Types I-III + - A list of non-negative real numbers for Type IV points EXAMPLES:: @@ -484,25 +510,6 @@ def diameter(self): return radius_expr.limit(x="oo") return self._radius - def hyperbolic_metric(self, other): - """ - The hyperbolic distance between this point and ``other``. - - Also known as the path distance metric, or the big metric. - See path_distance_metric for details. - """ - return self.path_distance_metric(other) - - def big_metric(self, other): - """ - The big metric distance between this point and ``other``. - - Also known as the hyperbolic metric, or the path distance - metric. See path_distance_metric for details. - - """ - return self.path_distance_metric(other) - def path_distance_metric(self, other): """ Returns the path distance metric distance between ``self`` and ``other``. @@ -511,13 +518,14 @@ def path_distance_metric(self, other): On the set of Type II, III and IV points, the path distance metric is a metric. Following Baker and Rumely, we extend - the path distance metric to Type I points x,y by p(x,x) = 0 and p(x,y) = infty + the path distance metric to Type I points x,y by p(x,x) = 0 and p(x,y) = + infinity. INPUT: - ``other`` -- A point of the same Berkovich space as this point - OUTPUT: A real number, or the infinity symbol 'oo' + OUTPUT: A real number, or infinity EXAMPLES:: @@ -531,30 +539,36 @@ def path_distance_metric(self, other): sage: Q3 = B(1) sage: Q3.path_distance_metric(Q1) - 'oo' + +Infinity """ if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("Path distance metric not defined between point of " + \ + raise ValueError("path distance metric not defined between point of " + \ "Berkovich space and %s" %(other)) if self.parent() != other.parent(): - raise ValueError("Input to path distance metric was an element of a " + \ - "different Berkovich space") + raise ValueError("input was an element of a different Berkovich space") if self.type_of_point() == 1 or other.type_of_point() == 1: if self == other: return 0 else: - return "oo" + return Infinity return 2*((self.join(other)).diameter().log(self.prime()))\ -self.diameter().log(self.prime())\ -other.diameter().log(other.prime()) + big_metric = path_distance_metric + + hyperbolic_metric = path_distance_metric + def small_metric(self, other): - """ + r""" Returns the small metric distance between this point and ``other``. + The small metric is an extension of twice + the spherical distance on P^1(\CC_p) + INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number @@ -568,10 +582,10 @@ def small_metric(self, other): """ if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("Hyperbolic metric not defined between point " + \ + raise ValueError("hyperbolic metric not defined between point " + \ "of Berkovich space and %s" %(other)) if self.parent() != other.parent(): - raise ValueError("Input to hyperbolic metric was an element " + \ + raise ValueError("input to hyperbolic metric was an element " + \ "of a different Berkovich space") gauss = self.parent()(RR(0),RR(1)) g_greater_than_s = gauss.partial_order(self) @@ -590,14 +604,16 @@ def small_metric(self, other): -new_self.diameter() \ -new_other.diameter() - def Hsia_kernel_infinity(self, other): - """ + r""" Return the Hsia kernel at infinity of this point with ``other``. + The Hsia kernel at infinity is the natural extension of the + absolute value on \CC_p to Berkovich space. + INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point OUTPUT: A real number @@ -613,11 +629,12 @@ def Hsia_kernel_infinity(self, other): return self.join(other).diameter() def center(self): - """ - Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. + r""" + Returns the center of the corresponding disk (or sequence of disks) + in ``\CC_p``. OUTPUT: An element of the ``base_ring`` of the parent Berkovich - Space. + space. EXAMPLES:: @@ -671,12 +688,26 @@ def prime(self): def __ne__(self, other): """ Non-equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(3,3**(1/2)) + sage: Q2 = B(3,RR(3**(1/2))) + sage: Q1 != Q2 + False """ return not (self == other) def _repr_(self): """ String representation of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: B(2,1) + Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 """ if self._type == 1: return "Type I point centered at " + format(self._center) @@ -699,8 +730,16 @@ def _repr_(self): %(self._center_lst[:2],self._radius_lst[:2]) def _latex_(self): - """ + r""" LaTeX representation of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: latex(B(2,1)) + \text{Type 2 Point of } \text{Projective Berkovich line over } + \Bold{C}_{3} \text{equivalent to the disk centered at (2 + O(3^20) : 1 + O(3^20)) + of radius 1.00000000000000 in } \Bold{C}_3 """ from sage.misc.latex import latex if self._type == 1: @@ -709,20 +748,11 @@ def _latex_(self): text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" \ %(self._center, self._radius, self._p) else: - text = r"the sequence of disks with centers %s } " %self._center_lst[:2] + \ + text = "the sequence of disks with centers %s } " %self._center_lst[:2] + \ r"\ldots \text{ and radii %s } \ldots" %self._radius_lst[:2] return r"\text{Type %s Point of }" %(self._type) \ + latex(self.parent()) + r"\text{equivalent to " + text - def __hash__(self): - """ - Return the hash of this point. - """ - if self.type_of_point() != 4: - return hash(str(self.center())+str(self.radius())) - return hash(str(self.center()[self.precision()])+str(self.radius()[self.precision()])) - - class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" Element class of the Berkovich affine line over `\CC_p` @@ -882,7 +912,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check except: flag = True if flag: - raise ValueError("The point at infinity of Berkovich " + \ + raise ValueError("the point at infinity of Berkovich " + \ "Projective space does not convert to Berkovich Affine space") self._center = center.center()[0] self._radius = center._radius @@ -899,7 +929,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("Tried to convert from a point of Berkovich space " + \ + raise ValueError("tried to convert from a point of Berkovich space " + \ "over Cp(%s) to a " %center._base_space.base_ring().prime() + \ "point of Berkovich space over Cp(%s)" %(parent.base().prime())) @@ -933,6 +963,14 @@ def center(self): def __eq__(self, other): """ Equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(1, RR(3**(1/2))) + sage: Q2 = B(1, 3**(1/2)) + sage: Q1 == Q2 + True """ if other is self: return True @@ -957,8 +995,18 @@ def __eq__(self, other): def __hash__(self): """ Returns the hash of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(1, RR(3**(1/2))) + sage: Q2 = B(1, 3**(1/2)) + sage: hash(Q1) == hash(Q2) + True """ - return super().__hash__() + if self.type_of_point() != 4: + return hash(str(self.center())+str(self.radius())) + return hash(str(self.center()[self.precision()])+str(self.radius()[self.precision()])) def partial_order(self,other): """ @@ -1135,7 +1183,7 @@ def involution_map(self): if self.type_of_point() == 1: if center == 0: - raise ValueError("Involution map not deffined on Affine Type I point centered at 0") + raise ValueError("involution map not deffined on Affine Type I point centered at 0") return parent(1/center) zero = parent(QQ(0)) @@ -1203,7 +1251,6 @@ def contained_in_interval(self, start, end): if end.parent() != self.parent(): raise ValueError("start must be a point of the same Berkovich space as this point") - #we treat infinity as a special case base = self._base_space P = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) return P(self).contained_in_interval(P(start),P(end)) @@ -1212,6 +1259,9 @@ def potential_kernel(self, other, basepoint): """ The potential kernel of this point with ``other``, with basepoint ``basepoint``. + The potential kernel is the hyperbolic distance between the basepoint and the join + of this point with ``other`` relative to the basepoint. + INPUT: - ``other`` -- A point of the same Berkovich space as this point @@ -1277,6 +1327,8 @@ def Hsia_kernel(self, other, basepoint): """ The Hsia kernel of this point and ``other``, with basepoint ``basepoint``. + The Hsia kernel is a generalization of the Hsia kernel at + infinity. INPUT: - ``other`` -- A point of the same Berkovich space as this point @@ -1334,7 +1386,7 @@ def diameter(self, basepoint="oo"): - ``basepoint`` -- (default = Infinity) A point of the same Berkovich space as this point - OUTPUT: A real number or the infinity symbol 'oo' + OUTPUT: A real number or Infinity EXAMPLES:: @@ -1449,12 +1501,12 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) and radii given by 40.0000000000000*pi/x - TEST:: + TESTS:: sage: P((1,0),3) Traceback (most recent call last): ... - ValueError: Type II and III points cannot be centered at (1 : 0) + ValueError: type II and III points cannot be centered at (1 : 0) """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): @@ -1483,7 +1535,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("Tried to convert from a point of Berkovich space over " + \ + raise ValueError("tried to convert from a point of Berkovich space over " + \ "Cp(%s) to a point of Berkovich space over Cp(%s)" \ %(center._base_space.base_ring().prime(),parent.base().prime())) @@ -1517,6 +1569,14 @@ def center(self): def __eq__(self, other): """ Equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B([2,2], RR(3**(1/2))) + sage: Q2 = B([1,1], 3**(1/2)) + sage: Q1 == Q2 + True """ if other is self: return True @@ -1529,7 +1589,7 @@ def __eq__(self, other): if stype == otype and stype == 1: return self.center() == other.center() elif stype == otype and stype == 4: - raise NotImplementedError("Equality for Type IV points not yet implemented") + raise NotImplementedError("equality for Type IV points not yet implemented") elif stype in [2,3] and otype in [2,3]: if self.radius() != other.radius(): return False @@ -1543,8 +1603,20 @@ def __eq__(self, other): def __hash__(self): """ Returns the hash of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: P = B.base_ring() + sage: Q1 = B(P.point([2,2],False), RR(3**(1/2))) + sage: Q2 = B([1,1], 3**(1/2)) + sage: hash(Q1) == hash(Q2) + True """ - return super().__hash__() + if self.type_of_point() != 4: + return hash(str(self.center().normalize_coordinates())+str(self.radius())) + return hash(str((self.center()[self.precision()]).normalize_coordinates())\ + +str(self.radius()[self.precision()])) def partial_order(self,other): """ @@ -1923,6 +1995,10 @@ def potential_kernel(self, other, basepoint): The potential kernel of this point with ``other``, with basepoint ``basepoint``. + The potential kernel is the hyperbolic distance + between ``basepoint`` and the join of this point + with ``other`` relative to ``basepoint``. + INPUT: - ``other`` -- A point of the same Berkovich space as this point @@ -1989,12 +2065,15 @@ def Hsia_kernel(self, other, basepoint): The Hsia kernel of this point and ``other``, with basepoint ``basepoint``. + The Hsia kernel with arbitrary basepoint + is a generalization of the Hsia kernel at infinity. + INPUT: - ``other`` -- A point of the same Berkovich space as this point - ``basepoint`` -- A point of the same Berkovich space as this point - OUTPUT: A real number or the infinity symbol 'oo' + OUTPUT: A real number or infinity EXAMPLES:: @@ -2005,14 +2084,14 @@ def Hsia_kernel(self, other, basepoint): sage: Q1.Hsia_kernel(Q2,Q3) 0.111111111111111 - :: + :: sage: B = Berkovich_Cp_Projective(3) sage: Q1 = B(2,9) sage: Q2 = B(1/2) sage: Q3 = B(1/2) sage: Q1.Hsia_kernel(Q2,Q3) - 'oo' + +Infinity """ if not isinstance(other,Berkovich_Element_Cp_Projective): @@ -2026,7 +2105,7 @@ def Hsia_kernel(self, other, basepoint): raise ValueError("basepoint must be a point of the same Berkovich Projective line") if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: - return "oo" + return Infinity return (self.spherical_kernel(other))/ \ (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) @@ -2046,7 +2125,7 @@ def diameter(self, basepoint="oo"): - ``basepoint`` -- (default = Infinity) A point of the same Berkovich space as this point, or the string 'oo' - OUTPUT: A real number or the infinity symbol 'oo' + OUTPUT: A real number or infinity EXAMPLES:: @@ -2059,7 +2138,7 @@ def diameter(self, basepoint="oo"): :: sage: Q2.diameter(Q2) - 'oo' + +Infinity """ if basepoint == "oo": return super().diameter() @@ -2078,13 +2157,34 @@ class Berkovich_Cp(Berkovich): """ def __eq__(self,right): - + """ + Equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: A. = Qq(27) + sage: C = Berkovich_Cp_Affine(A) + sage: B == C + True + """ if not isinstance(right, Berkovich_Cp): return False - return self.base() == right.base() + return self.prime() == right.prime() + + def __ne__(self,right): + """ + Inequality operator. - def __neq__(self,right): - return not self == right + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(5) + sage: A. = Qq(25) + sage: C = Berkovich_Cp_Affine(A) + sage: B != C + False + """ + return not(self == right) class Berkovich_Cp_Affine(Berkovich_Cp): r""" @@ -2133,9 +2233,9 @@ def __init__(self,base): if base.is_prime(): base = Qp(base) #TODO chance to Qpbar else: - raise ValueError("Non-prime pased into Berkovich space") + raise ValueError("non-prime pased into Berkovich space") if not is_pAdicField(base): #TODO change base to Qpbar(prime) - raise ValueError("Base of Berkovich Space must be a padic field") + raise ValueError("base of Berkovich Space must be a padic field") self._p = base.prime() Parent.__init__(self, base = base, category=TopologicalSpaces()) @@ -2236,19 +2336,19 @@ def __init__(self,base): Berkovich_Cp_Projective.__init__(self,base) return else: - raise ValueError("Non-prime pased into Berkovich space") + raise ValueError("non-prime pased into Berkovich space") else: from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(base): - raise ValueError("Base of Projective Berkovich Space must be Projective Space") + raise ValueError("base of Projective Berkovich Space must be Projective Space") if not (is_pAdicField(base.base_ring())): - raise ValueError("Base of Projective Berkovich Space must be " + \ + raise ValueError("base of Projective Berkovich Space must be " + \ "Projective Space over Qp") if base.ambient_space() != base: - raise ValueError("Base of Projective Berkovich Space must be " + \ + raise ValueError("base of Projective Berkovich Space must be " + \ "Projective Space over Qp") if base.dimension() != 1: - raise ValueError("Base of Projective Berkovich Space must be " + \ + raise ValueError("base of Projective Berkovich Space must be " + \ "Projective Space of dimension 1 over Qp") self._p = base.base_ring().prime() Parent.__init__(self, base = base, category=TopologicalSpaces()) From 90acf0defde0658087ea6113ce6fa66d46b2af28 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 23 Jun 2020 10:41:41 -0400 Subject: [PATCH 015/379] added some error checking --- .../arithmetic_dynamics/berkovich_ds.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 7925c709bf3..6e5d060a4f5 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -7,12 +7,21 @@ projective Berkovich space. """ +#***************************************************************************** +# 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.element import Element from sage.dynamics.arithmetic_dynamics.generic_ds import DynamicalSystem from sage.misc.classcall_metaclass import typecall from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.rings.padics.generic_nodes import is_pAdicField from sage.schemes.berkovich.berkovich_space import Berkovich_Cp_Affine, Berkovich_Cp_Projective +from sage.dynamics.arithmetic_dynamics.projective_ds import DynamicalSystem_projective class DynamicalSystem_Berkovich(Element): @@ -29,6 +38,7 @@ def __classcall_private__(cls, system_morphism_polys, domain=None): - ``domain`` -- (optional) affine or projective Berkovich space over \CC_p """ + if not(domain is None) or if isinstance(system_morphism_polys,(list,tuple)): polys = list(system_morphism_polys) if len(system_morphism_polys) not in [1,2]: @@ -41,6 +51,15 @@ def __classcall_private__(cls, system_morphism_polys, domain=None): polys = [poly.lift() for poly in polys] except AttributeError: raise ValueError('{} must be elements of a polynomial ring'.format(system_morphism_polys)) + if domain is None: + PR = get_coercion_model().common_parent(*polys) + if (not is_pAdicField(PR)) or : + raise ValueError('common parent for polynomials not a padic field') + system_morphism_polys = DynamicalSystem_projective(polys) + else: + + if isinstance(domain, Berkovich_Cp_Affine): + if isinstance(system_morphism_polys, DynamicalSystem): system_morphism_polys = system_morphism_polys.as_scheme_morphism() @@ -52,7 +71,10 @@ def __classcall_private__(cls, system_morphism_polys, domain=None): if morphism_domain != system_morphism_polys.codomain(): raise ValueError('domain and codomain do not agree') if not is_pAdicField(R): - raise ValueError('system_morphism_polys not defined over padic field') + if domain is None: + raise ValueError('system_morphism_polys not defined over padic field and domain is None') + try: + system_morphism_polys = system_morphism_polys.change_ring() if morphism_domain != morphism_domain.ambient_space(): raise ValueError('morphism must be defined on the ambient space') if domain == None: From d14d11387e902964167b2f113965343dc502e978 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 23 Jun 2020 10:45:10 -0400 Subject: [PATCH 016/379] 29844: added is_Berkovich and is_Berkovich_Cp --- src/sage/schemes/berkovich/berkovich_space.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 3964583ef0d..c195ddd06ac 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -47,6 +47,29 @@ from sage.rings.rational_field import QQ from sage.rings.infinity import Infinity +def is_Berkovich(space): + """ + Checks if ``space`` is a Berkovich space. + + OUTPUT: + + - ``True`` if ``space`` is a Berkovich space + - ``False`` otherwise + """ + return isinstance(space, Berkovich) + +def is_Berkovich_Cp(space): + """ + Checks if ``space`` is a Berkovich space over ``Cp``. + + OUTPUT: + + - ``True`` if ``space`` is a Berkovich space over ``Cp`` + - ``False`` otherwise + """ + return isinstance(space, Berkovich_Cp) + + class Berkovich_Element(Element): """ The parent class for any element of a Berkovich space From 7ed429ffa7869a1b48695122e6c7c5f0cd4fbe8e Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 23 Jun 2020 15:09:35 -0400 Subject: [PATCH 017/379] 29844: trivial fix --- src/sage/schemes/berkovich/berkovich_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index c195ddd06ac..a24266d74f7 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -180,7 +180,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("the center of a point of Projective Berkovich" + \ "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) elif not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(),pAdicGeneric): + if not isinstance(center.scheme().base_ring(),pAdicBaseGeneric): try: center = (self._base_space)(center) flag = False From 4668fcac8074e249866697618d1c291ca2773348 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 23 Jun 2020 19:56:55 -0400 Subject: [PATCH 018/379] 29949: initial commit. can create dynamical system + image of Type I points --- src/sage/dynamics/arithmetic_dynamics/all.py | 1 + .../arithmetic_dynamics/berkovich_ds.py | 158 +++++++++++++++--- src/sage/schemes/berkovich/berkovich_space.py | 25 ++- 3 files changed, 156 insertions(+), 28 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/all.py b/src/sage/dynamics/arithmetic_dynamics/all.py index b434f8001bc..c87cba0d3d9 100644 --- a/src/sage/dynamics/arithmetic_dynamics/all.py +++ b/src/sage/dynamics/arithmetic_dynamics/all.py @@ -5,5 +5,6 @@ from .affine_ds import DynamicalSystem_affine from .projective_ds import DynamicalSystem_projective from .product_projective_ds import DynamicalSystem_product_projective +from .berkovich_ds import DynamicalSystem_Berkovich lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'WehlerK3Surface') lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'random_WehlerK3Surface') diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 6e5d060a4f5..0d6da6e5e63 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -17,12 +17,22 @@ from sage.structure.element import Element from sage.dynamics.arithmetic_dynamics.generic_ds import DynamicalSystem +from six import add_metaclass +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.classcall_metaclass import typecall from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.rings.padics.generic_nodes import is_pAdicField -from sage.schemes.berkovich.berkovich_space import Berkovich_Cp_Affine, Berkovich_Cp_Projective +from sage.schemes.berkovich.berkovich_space import (Berkovich_Cp_Affine, + Berkovich_Cp_Projective, is_Berkovich_Cp, Berkovich_Element_Cp) +from sage.rings.padics.factory import Qp +from sage.structure.element import get_coercion_model +from sage.schemes.projective.projective_space import ProjectiveSpace +from sage.schemes.affine.affine_space import is_AffineSpace +from sage.rings.padics.padic_base_generic import pAdicBaseGeneric from sage.dynamics.arithmetic_dynamics.projective_ds import DynamicalSystem_projective +from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine +@add_metaclass(InheritComparisonClasscallMetaclass) class DynamicalSystem_Berkovich(Element): @staticmethod @@ -38,52 +48,146 @@ def __classcall_private__(cls, system_morphism_polys, domain=None): - ``domain`` -- (optional) affine or projective Berkovich space over \CC_p """ - if not(domain is None) or + if not(domain is None or is_Berkovich_Cp(domain)): + raise ValueError('domain must be a Berkovich space over Cp') if isinstance(system_morphism_polys,(list,tuple)): - polys = list(system_morphism_polys) if len(system_morphism_polys) not in [1,2]: - raise ValueError('system_morphism_polys must be length 1 or 2') - from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing + raise ValueError('list of polynomials too long, must be length 1 or 2') from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing test = lambda x: is_PolynomialRing(x) or is_MPolynomialRing(x) + polys = list(system_morphism_polys) if not all(test(poly.parent()) for poly in polys): try: polys = [poly.lift() for poly in polys] except AttributeError: - raise ValueError('{} must be elements of a polynomial ring'.format(system_morphism_polys)) - if domain is None: - PR = get_coercion_model().common_parent(*polys) - if (not is_pAdicField(PR)) or : - raise ValueError('common parent for polynomials not a padic field') - system_morphism_polys = DynamicalSystem_projective(polys) + raise ValueError('{} must be elements of a polynomial ring'\ + .format(system_morphism_polys)) + for poly in polys: + if not isinstance(poly.base_ring(), pAdicBaseGeneric): + if domain is None: + raise ValueError('polynomials not defined over a padic with no specified domain') + try: + poly = poly.change_ring(Qp(domain.prime())) #TODO change to Qpbar + flag = False + except: + flag = True + if flag: + raise ValueError('{} does not convert to Qp'.format(poly)) + PR = get_coercion_model().common_parent(*polys) + if isinstance(domain, Berkovich_Cp_Affine): + if domain.prime() != PR.prime(): + raise ValueError('specified domain has an incorrect residue characteristic') + if len(polys) != 1: + raise ValueError('list of polynomials too long for affine Berkovich space') + if len(polys[0].gens()) != 1: + raise ValueError('too many variables for dynamical system' + \ + 'on affine Berkovich space') + system = DynamicalSystem_affine(polys) + return typecall(cls,system,domain) else: - - if isinstance(domain, Berkovich_Cp_Affine): - - - if isinstance(system_morphism_polys, DynamicalSystem): - system_morphism_polys = system_morphism_polys.as_scheme_morphism() + if domain is None: + P = ProjectiveSpace(PR, 1) + domain = Berkovich_Cp_Projective(P) + if domain.prime() != PR.prime(): + raise ValueError('specified domain has an incorrect residue characteristic') + if not (len(polys) in [1,2]): + raise ValueError('list of polynomials too long for affine Berkovich space') + if not(len(polys[0].gens()) in [1,2]): + raise ValueError('too many variables for dynamical system' + \ + 'on projective Berkovich space') + system = DynamicalSystem_projective(polys) + return typecall(cls,system,domain) if isinstance(system_morphism_polys, SchemeMorphism_polynomial): R = system_morphism_polys.base_ring() morphism_domain = system_morphism_polys.domain() - polys = list(system_morphism_polys) if morphism_domain != system_morphism_polys.codomain(): raise ValueError('domain and codomain do not agree') - if not is_pAdicField(R): + if not isinstance(R, pAdicBaseGeneric): if domain is None: raise ValueError('system_morphism_polys not defined over padic field and domain is None') try: - system_morphism_polys = system_morphism_polys.change_ring() + #TODO change to Qpbar + system_morphism_polys = system_morphism_polys.change_ring(Qp(domain.prime())) + morphism_domain = system_morphism_polys.domain() + R = system_morphism_polys.base_ring() + flag = False + except: + flag = True + if flag: + raise ValueError('system_morphism_polys could not be converted to Qp(%s)' %domain.prime()) if morphism_domain != morphism_domain.ambient_space(): raise ValueError('morphism must be defined on the ambient space') - if domain == None: - domain = Berkovich_Cp_Projective(morphism_domain) + if morphism_domain.dimension_absolute() != 1: + raise ValueError('domain not dimension 1') + if is_AffineSpace(morphism_domain): + domain = Berkovich_Cp_Affine(R) else: - from sage.schemes.berkovich.berkovich_space import Berkovich_Cp - if not isinstance(domain, Berkovich_Cp): - raise ValueError('domain must be Berkovich space over Cp') - return typecall(cls,polys,domain) - + domain = Berkovich_Cp_Projective(ProjectiveSpace(R, 1)) + if not isinstance(system_morphism_polys, DynamicalSystem): + system_morphism_polys = DynamicalSystem(system_morphism_polys) + return typecall(cls,system_morphism_polys,domain) + raise ValueError('system_morphism_polys was not a dynamical system, a morphism, or a polynomial') + + def __init__(self, dynamical_system, domain): + r""" + The Python constructor + """ + self._system = dynamical_system + self._polys = dynamical_system._polys + self._domain = domain + + def domain(self): + """ + Returns the domain of this dynamical system. + + OUTPUT: A Berkovich space over ``Cp`` + EXAMPLES:: + + sage: Q. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([3*x^2,2*y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: g.domain() + Projective Berkovich line over Cp(3) of precision 20 + """ + return self._domain + + def __call__(self, x): + """ + Makes dynamical systems on Berkovich space over ``Cp`` callable. + + INPUT: + + - ``x`` -- a point of Berkovich space over ``Cp`` + + EXAMPLES:: + """ + if not isinstance(x.parent(), type(self._domain)): + raise ValueError('action of dynamical system not defined on %s' %x) + if x.type_of_point() != 1: + raise NotImplementedError('action on Type II, III, and IV points not implemented') + return self._system(x.center()) + + def _repr_(self): + """ + Return a string representation of this dynamical system. + + OUTPUT: a string + + EXAMPLES:: + + sage: Q. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([3*x^2,2*y^2]) + sage: f = DynamicalSystem_Berkovich(f) + sage: f._repr_() + 'Dynamical system of Projective Space of dimension 1 over + 3-adic Field with capped relative precision 20 induced by the map + \n Defn: Defined on coordinates by sending (x : y) to\n + ((3 + O(3^21))*x^2 : (2 + O(3^20))*y^2)' + """ + domain_str = self._domain._repr_() + return "Dynamical system of " + domain_str + " induced by the map " + \ + "\n Defn: %s"%('\n '.join(self._system._repr_defn().split('\n'))) \ No newline at end of file diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 3964583ef0d..a24266d74f7 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -47,6 +47,29 @@ from sage.rings.rational_field import QQ from sage.rings.infinity import Infinity +def is_Berkovich(space): + """ + Checks if ``space`` is a Berkovich space. + + OUTPUT: + + - ``True`` if ``space`` is a Berkovich space + - ``False`` otherwise + """ + return isinstance(space, Berkovich) + +def is_Berkovich_Cp(space): + """ + Checks if ``space`` is a Berkovich space over ``Cp``. + + OUTPUT: + + - ``True`` if ``space`` is a Berkovich space over ``Cp`` + - ``False`` otherwise + """ + return isinstance(space, Berkovich_Cp) + + class Berkovich_Element(Element): """ The parent class for any element of a Berkovich space @@ -157,7 +180,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("the center of a point of Projective Berkovich" + \ "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) elif not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(),pAdicGeneric): + if not isinstance(center.scheme().base_ring(),pAdicBaseGeneric): try: center = (self._base_space)(center) flag = False From d6af56cf8bf65b98a6f17ebf23e4b744ac6d64e4 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 23 Jun 2020 19:58:24 -0400 Subject: [PATCH 019/379] 29949: fixed minor bug with image of Type I --- src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 0d6da6e5e63..48f8e575926 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -169,7 +169,7 @@ def __call__(self, x): raise ValueError('action of dynamical system not defined on %s' %x) if x.type_of_point() != 1: raise NotImplementedError('action on Type II, III, and IV points not implemented') - return self._system(x.center()) + return self.domain()(self._system(x.center())) def _repr_(self): """ From ce176ef345a9bc38abaac2ec5433729cfa0c54a0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 23 Jun 2020 21:34:41 +0200 Subject: [PATCH 020/379] make coding fuzz ready --- src/sage/coding/grs_code.py | 8 +++++++- src/sage/coding/linear_code.py | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 427c96fced9..3c4d9bad16a 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -441,8 +441,14 @@ def dual_code(self): EXAMPLES:: + sage: def rand_non_zero(F): + ....: x = F.random_element() + ....: while x == 0: + ....: x = F.random_element() + ....: return x + sage: F = GF(59) - sage: colmults = [ F.random_element() for i in range(40) ] + sage: colmults = [ rand_non_zero(F) for i in range(40) ] sage: C = codes.GeneralizedReedSolomonCode(F.list()[:40], 12, colmults) sage: Cd = C.dual_code(); Cd [40, 28, 13] Generalized Reed-Solomon Code over GF(59) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 4b8f04dc4ed..620c143ae0d 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -2546,7 +2546,7 @@ class LinearCodeSyndromeDecoder(Decoder): sage: D = C.decoder("Syndrome", maximum_error_weight = 5) # long time sage: D.decoder_type() # long time {'complete', 'hard-decision', 'might-error'} - sage: D.decoding_radius() # long time + sage: D.decoding_radius() # long time 4 In that case, the decoder might still return an unexpected codeword, but @@ -2807,7 +2807,7 @@ def decode_to_code(self, r): sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), 2) sage: c = C.random_element() sage: r = Chan(c) - sage: c == D.decode_to_code(r) + sage: c == D.decode_to_code(r) # known bug True """ lookup_table = self.syndrome_table() From 9dcd35c4fc36ecb2483f92ec37a8918df743b014 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 24 Jun 2020 11:25:38 +0200 Subject: [PATCH 021/379] fix doctest in sage/coding/linear_code --- src/sage/coding/linear_code.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 620c143ae0d..545b230a29c 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -2803,8 +2803,8 @@ def decode_to_code(self, r): ....: [0, 0, 1, 0, 2, 0, 0, 2], ....: [0, 0, 0, 1, 0, 2, 0, 1]]) sage: C = LinearCode(G) - sage: D = codes.decoders.LinearCodeSyndromeDecoder(C, maximum_error_weight = 2) - sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), 2) + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C, maximum_error_weight = 1) + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), 1) sage: c = C.random_element() sage: r = Chan(c) sage: c == D.decode_to_code(r) # known bug From f259cea3d3deaabae4c53e3a019a0bedf56b3583 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 24 Jun 2020 22:50:16 +0200 Subject: [PATCH 022/379] remove known bug flag --- src/sage/coding/linear_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 545b230a29c..730589534c6 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -2807,7 +2807,7 @@ def decode_to_code(self, r): sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), 1) sage: c = C.random_element() sage: r = Chan(c) - sage: c == D.decode_to_code(r) # known bug + sage: c == D.decode_to_code(r) True """ lookup_table = self.syndrome_table() From b57d7099b6f8fae21bba6c55216e366ee4646966 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 24 Jun 2020 23:20:08 +0200 Subject: [PATCH 023/379] marking bugs --- src/sage/coding/grs_code.py | 2 +- src/sage/coding/punctured_code.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 3c4d9bad16a..a0c663c1447 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -1944,7 +1944,7 @@ def decode_to_message(self, word_and_erasure_vector): sage: n_era = randint(0, C.minimum_distance() - 2) sage: Chan = channels.ErrorErasureChannel(C.ambient_space(), D.decoding_radius(n_era), n_era) sage: y = Chan(c) - sage: D.connected_encoder().unencode(c) == D.decode_to_message(y) + sage: D.connected_encoder().unencode(c) == D.decode_to_message(y) # known bug True TESTS: diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index 36a881d6d0a..33d3e7fac6f 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -625,7 +625,7 @@ def decode_to_code(self, y): sage: y = Chan(c) sage: y in Cp False - sage: D.decode_to_code(y) == c + sage: D.decode_to_code(y) == c # known bug True """ D = self.original_decoder() From 1633f58ab5e612ba6567e2ea9dcb7327b13a559f Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 24 Jun 2020 19:31:46 -0400 Subject: [PATCH 024/379] 29949: seperated systems over projective and affine space --- .../arithmetic_dynamics/berkovich_ds.py | 380 +++++++++++++----- 1 file changed, 281 insertions(+), 99 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 48f8e575926..c6cb69623a8 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -34,106 +34,124 @@ @add_metaclass(InheritComparisonClasscallMetaclass) class DynamicalSystem_Berkovich(Element): + r""" + A dynamical system on Berkovich space over `\CC_p`. - @staticmethod - def __classcall_private__(cls, system_morphism_polys, domain=None): - r""" - Base class for dynamical systems on Berkovich space over \CC_p. + A dynamical system on Berkovich space over `\CC_p` is + determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, + which naturally induces a dynamical system on affine or + projective Berkovich space. + + INPUT:: + + - ``dynamical_system`` -- any input which defines a dynamical + system over affine or projective space, or a dynamical system + over affine or projective space. If the input is a list + of homogenous polynomials, then ``domain`` is taken to + be projective Berkovich space, unless specified. + If this input is not defined over a padic field, + then ``domain`` MUST be specified. + + - ``domain`` -- (optional) affine or projective Berkovich space + over `\CC_p`. If the input to ``dynamical_system`` is + not defined over `\QQ_p` or a finite extension, ``domain`` + must be specified. + + EXAMPLES: + + We can easily create a dynamical system on Berkovich space + using a dynamical system on projective space over `\QQ_p`:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([2*x^2 + 4*y^2, 3*x^2 + 9*y^2]) + sage: DynamicalSystem_Berkovich(f) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + ((2 + O(3^20))*x^2 + (1 + 3 + O(3^20))*y^2 : (3 + O(3^21))*x^2 + (3^2 + O(3^22))*y^2) + + Or from a morphism:: + + sage: P1. = ProjectiveSpace(Qp(3),1) + sage: H = End(P1) + sage: DynamicalSystem_Berkovich(H([y, x])) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + (y : x) + + Or from polynomials:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: DynamicalSystem_Berkovich([x^2+y^2, y^2]) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + (x^2 + y^2 : y^2) + + Note that the default behavior on polynomial input is to construct + a dynamical system on the projective line. To construct a dynamical + system on on the affine line, specify ``domain``:: + + sage: A. = AffineSpace(Qp(3),1) + sage: B = Berkovich_Cp_Affine(3) + sage: DynamicalSystem_Berkovich([x^2+1], B) + Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x) to + (x^2 + 1 + O(3^20)) + + Creating a map on Berkovich space creates the Berkovich space + it acts on:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2, y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: B = g.domain(); B + Projective Berkovich line over Cp(3) of precision 20 + + We can take the image of points of the domain:: - INPUT:: + sage: Q1 = B(2) + sage: g(Q1) + Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) + """ - - ``system_morphism_polys`` -- a list of polynomials or rational functions, - or a dynamical system. In any case, this input should be - defined over affine or projective space of a finite extension of \QQ_p. + @staticmethod + def __classcall_private__(cls, dynamical_system, domain=None): + """ + Returns the appropriate dynamical system on Berkovich space. - - ``domain`` -- (optional) affine or projective Berkovich space over \CC_p + EXAMPLES:: + + sage: R. = QQ[] + sage: B = Berkovich_Cp_Affine(3) + sage: DynamicalSystem_Berkovich(t^2 - 3,B) + Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (t) to + (t^2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + 2*3^14 + 2*3^15 + 2*3^16 + 2*3^17 + 2*3^18 + 2*3^19 + 2*3^20 + O(3^21)) """ if not(domain is None or is_Berkovich_Cp(domain)): raise ValueError('domain must be a Berkovich space over Cp') - if isinstance(system_morphism_polys,(list,tuple)): - if len(system_morphism_polys) not in [1,2]: - raise ValueError('list of polynomials too long, must be length 1 or 2') - from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing - test = lambda x: is_PolynomialRing(x) or is_MPolynomialRing(x) - polys = list(system_morphism_polys) - if not all(test(poly.parent()) for poly in polys): - try: - polys = [poly.lift() for poly in polys] - except AttributeError: - raise ValueError('{} must be elements of a polynomial ring'\ - .format(system_morphism_polys)) - for poly in polys: - if not isinstance(poly.base_ring(), pAdicBaseGeneric): - if domain is None: - raise ValueError('polynomials not defined over a padic with no specified domain') - try: - poly = poly.change_ring(Qp(domain.prime())) #TODO change to Qpbar - flag = False - except: - flag = True - if flag: - raise ValueError('{} does not convert to Qp'.format(poly)) - PR = get_coercion_model().common_parent(*polys) + if isinstance(dynamical_system, SchemeMorphism_polynomial): + morphism_domain = dynamical_system.domain() + + if not domain is None: if isinstance(domain, Berkovich_Cp_Affine): - if domain.prime() != PR.prime(): - raise ValueError('specified domain has an incorrect residue characteristic') - if len(polys) != 1: - raise ValueError('list of polynomials too long for affine Berkovich space') - if len(polys[0].gens()) != 1: - raise ValueError('too many variables for dynamical system' + \ - 'on affine Berkovich space') - system = DynamicalSystem_affine(polys) - return typecall(cls,system,domain) - else: - if domain is None: - P = ProjectiveSpace(PR, 1) - domain = Berkovich_Cp_Projective(P) - if domain.prime() != PR.prime(): - raise ValueError('specified domain has an incorrect residue characteristic') - if not (len(polys) in [1,2]): - raise ValueError('list of polynomials too long for affine Berkovich space') - if not(len(polys[0].gens()) in [1,2]): - raise ValueError('too many variables for dynamical system' + \ - 'on projective Berkovich space') - system = DynamicalSystem_projective(polys) - return typecall(cls,system,domain) - - if isinstance(system_morphism_polys, SchemeMorphism_polynomial): - R = system_morphism_polys.base_ring() - morphism_domain = system_morphism_polys.domain() - if morphism_domain != system_morphism_polys.codomain(): - raise ValueError('domain and codomain do not agree') - if not isinstance(R, pAdicBaseGeneric): - if domain is None: - raise ValueError('system_morphism_polys not defined over padic field and domain is None') - try: - #TODO change to Qpbar - system_morphism_polys = system_morphism_polys.change_ring(Qp(domain.prime())) - morphism_domain = system_morphism_polys.domain() - R = system_morphism_polys.base_ring() - flag = False - except: - flag = True - if flag: - raise ValueError('system_morphism_polys could not be converted to Qp(%s)' %domain.prime()) - if morphism_domain != morphism_domain.ambient_space(): - raise ValueError('morphism must be defined on the ambient space') - if morphism_domain.dimension_absolute() != 1: - raise ValueError('domain not dimension 1') - if is_AffineSpace(morphism_domain): - domain = Berkovich_Cp_Affine(R) - else: - domain = Berkovich_Cp_Projective(ProjectiveSpace(R, 1)) - if not isinstance(system_morphism_polys, DynamicalSystem): - system_morphism_polys = DynamicalSystem(system_morphism_polys) - return typecall(cls,system_morphism_polys,domain) - - raise ValueError('system_morphism_polys was not a dynamical system, a morphism, or a polynomial') + return DynamicalSystem_Berkovich_affine(dynamical_system,domain) + from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine + if is_AffineSpace(morphism_domain) or isinstance(domain, AlgebraicScheme_subscheme_affine): + return DynamicalSystem_Berkovich_affine(dynamical_system,domain) + + return DynamicalSystem_Berkovich_projective(dynamical_system,domain) def __init__(self, dynamical_system, domain): r""" The Python constructor + + TESTS:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([2*x^2 + 4*y^2, 3*x^2 + 9*y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: isinstance(g, DynamicalSystem_Berkovich) + True """ self._system = dynamical_system self._polys = dynamical_system._polys @@ -164,15 +182,45 @@ def __call__(self, x): - ``x`` -- a point of Berkovich space over ``Cp`` EXAMPLES:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2, y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: B = g.domain() + sage: Q1 = B(2) + sage: g(Q1) + Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) """ if not isinstance(x.parent(), type(self._domain)): - raise ValueError('action of dynamical system not defined on %s' %x) - if x.type_of_point() != 1: - raise NotImplementedError('action on Type II, III, and IV points not implemented') - return self.domain()(self._system(x.center())) + raise ValueError('action of dynamical system not defined on %s' %x.parent()) + if x.type_of_point() == 1: + return self.domain()(self._system(x.center())) + if x.type_of_point() == 4: + raise NotImplementedError('action on Type IV points not implemented') + #TODO write a better check for zeros in disk - def _repr_(self): + def defining_polynomials(self): """ + Return the defining polynomials. + + OUTPUT: + + A tuple of polynomials that defines the + dynamical system. + + EXAMPLES:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([2*x^2 + 4*y^2, 3*x^2 + 9*y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: g.defining_polynomials() + ((2 + O(3^20))*x^2 + (1 + 3 + O(3^20))*y^2, + (3 + O(3^21))*x^2 + (3^2 + O(3^22))*y^2) + """ + return self._polys + + def _repr_(self): + r""" Return a string representation of this dynamical system. OUTPUT: a string @@ -183,11 +231,145 @@ def _repr_(self): sage: f = DynamicalSystem_projective([3*x^2,2*y^2]) sage: f = DynamicalSystem_Berkovich(f) sage: f._repr_() - 'Dynamical system of Projective Space of dimension 1 over - 3-adic Field with capped relative precision 20 induced by the map - \n Defn: Defined on coordinates by sending (x : y) to\n - ((3 + O(3^21))*x^2 : (2 + O(3^20))*y^2)' + 'Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map\n + Defn: Defined on coordinates by sending (x : y) to\n ((3 + O(3^21))*x^2 : (2 + O(3^20))*y^2)' """ domain_str = self._domain._repr_() - return "Dynamical system of " + domain_str + " induced by the map " + \ - "\n Defn: %s"%('\n '.join(self._system._repr_defn().split('\n'))) \ No newline at end of file + return "Dynamical system of " + domain_str + " induced by the map" + \ + "\n Defn: %s"%('\n '.join(self._system._repr_defn().split('\n'))) + +class DynamicalSystem_Berkovich_projective(DynamicalSystem_Berkovich): + r""" + A dynamical system on projective Berkovich space over `\CC_p`. + + A dynamical system on projective Berkovich space over `\CC_p` is + determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, + which naturally induces a dynamical system on affine or + projective Berkovich space. + + INPUT:: + + - ``dynamical_system`` -- any input which defines a dynamical + system over affine or projective space, or a dynamical system + over affine or projective space. If the input is a list + of homogenous polynomials, then ``domain`` is taken to + be projective Berkovich space, unless specified. + If this input is not defined over a padic field, + then ``domain`` MUST be specified. + + - ``domain`` -- (optional) affine or projective Berkovich space + over `\CC_p`. If the input to ``dynamical_system`` is + not defined over `\QQ_p` or a finite extension, ``domain`` + must be specified. + + EXAMPLES: + + We can easily create a dynamical system on Berkovich space + using a dynamical system on projective space over `\QQ_p`:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([1/2*x^2 + x*y + 3*y^2, 3*x^2 + 9*y^2]) + sage: DynamicalSystem_Berkovich_projective(f) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + ((2 + 3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20))*x^2 + x*y + (3 + O(3^21))*y^2 : (3 + O(3^21))*x^2 + (3^2 + O(3^22))*y^2) + + Or from a morphism:: + + sage: P1. = ProjectiveSpace(Qp(3),1) + sage: H = End(P1) + sage: DynamicalSystem_Berkovich_projective(H([y, x])) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + (y : x) + + Or from polynomials:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: DynamicalSystem_Berkovich_projective([x^2+y^2, y^2]) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + (x^2 + y^2 : y^2) + + Creating a map on Berkovich space creates the Berkovich space + it acts on:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2, y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: B = g.domain(); B + Projective Berkovich line over Cp(3) of precision 20 + + We can take the image of points of the domain:: + + sage: Q1 = B(2) + sage: g(Q1) + Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) + """ + @staticmethod + def __classcall_private__(cls, dynamical_system, domain=None): + """ + Returns the approapriate dynamical system on projective Berkovich space over ``Cp``. + """ + if not isinstance(dynamical_system, DynamicalSystem_projective): + dynamical_system = DynamicalSystem_projective(dynamical_system) + R = dynamical_system.base_ring() + morphism_domain = dynamical_system.domain() + if not isinstance(R, pAdicBaseGeneric): + if domain is None: + raise ValueError('dynamical system not defined over padic field and domain is None') + try: + #TODO change to Qpbar + dynamical_system = dynamical_system.change_ring(Qp(domain.prime())) + morphism_domain = dynamical_system.domain() + R = dynamical_system.base_ring() + flag = False + except: + flag = True + if flag: + raise ValueError('dynamical_system could not be converted to Qp(%s)' %domain.prime()) + if morphism_domain != morphism_domain.ambient_space(): + raise ValueError('morphism must be defined on the ambient space') + if morphism_domain.dimension_absolute() != 1: + raise ValueError('domain not dimension 1') + domain = Berkovich_Cp_Projective(ProjectiveSpace(R, 1)) + return typecall(cls,dynamical_system,domain) + + def __init__(self, dynamical_system, domain=None): + """ + Python constructor. + """ + DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) + +class DynamicalSystem_Berkovich_affine(DynamicalSystem_Berkovich): + @staticmethod + def __classcall_private__(cls, dynamical_system, domain=None): + if not isinstance(dynamical_system, DynamicalSystem_affine): + dynamical_system = DynamicalSystem_affine(dynamical_system) + R = dynamical_system.base_ring() + morphism_domain = dynamical_system.domain() + if not isinstance(R, pAdicBaseGeneric): + if domain is None: + raise ValueError('dynamical system not defined over padic field and domain is None') + try: + #TODO change to Qpbar + dynamical_system = dynamical_system.change_ring(Qp(domain.prime())) + morphism_domain = dynamical_system.domain() + R = dynamical_system.base_ring() + flag = False + except: + flag = True + if flag: + raise ValueError('dynamical_system could not be converted to Qp(%s)' %domain.prime()) + if morphism_domain != morphism_domain.ambient_space(): + raise ValueError('morphism must be defined on the ambient space') + if morphism_domain.dimension_absolute() != 1: + raise ValueError('domain not dimension 1') + domain = Berkovich_Cp_Affine(R) + return typecall(cls,dynamical_system,domain) + + def __init__(self, dynamical_system, domain): + """ + Python constructor. + """ + DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) \ No newline at end of file From 2e4a100ab17875dcad4d44eb740d6612056eaec0 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 25 Jun 2020 15:43:01 -0400 Subject: [PATCH 025/379] 29844: can pass padic field to projective space --- src/sage/schemes/berkovich/berkovich_space.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index a24266d74f7..c2cc1e165b1 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -2360,6 +2360,8 @@ def __init__(self,base): return else: raise ValueError("non-prime pased into Berkovich space") + elif is_pAdicField(base): + base = ProjectiveSpace(base, 1) else: from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(base): From 3231a76bda8288f29e7c1c7d9dc6ab6b3e8635a1 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 25 Jun 2020 16:03:54 -0400 Subject: [PATCH 026/379] 29844: base_ring for projective now returns a ring --- src/sage/schemes/berkovich/berkovich_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index c2cc1e165b1..69a9d0d91f3 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -2423,7 +2423,7 @@ def base_ring(self): Projective Space of dimension 1 over 3-adic Field with capped relative precision 1 """ - return self.base() + return self.base().base_ring() def _repr_(self): """ From e1e7732e190b3d520e17d919f6123949a58fe2b7 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 25 Jun 2020 19:46:30 -0400 Subject: [PATCH 027/379] 29949: image of Type II and III points under polynomial --- src/sage/dynamics/arithmetic_dynamics/all.py | 3 +- .../arithmetic_dynamics/berkovich_ds.py | 187 +++++++++++++++--- 2 files changed, 159 insertions(+), 31 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/all.py b/src/sage/dynamics/arithmetic_dynamics/all.py index c87cba0d3d9..5686ab3ba48 100644 --- a/src/sage/dynamics/arithmetic_dynamics/all.py +++ b/src/sage/dynamics/arithmetic_dynamics/all.py @@ -5,6 +5,7 @@ from .affine_ds import DynamicalSystem_affine from .projective_ds import DynamicalSystem_projective from .product_projective_ds import DynamicalSystem_product_projective -from .berkovich_ds import DynamicalSystem_Berkovich +from .berkovich_ds import (DynamicalSystem_Berkovich, DynamicalSystem_Berkovich_affine, + DynamicalSystem_Berkovich_projective) lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'WehlerK3Surface') lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'random_WehlerK3Surface') diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index c6cb69623a8..28765ae080a 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -23,7 +23,8 @@ from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.rings.padics.generic_nodes import is_pAdicField from sage.schemes.berkovich.berkovich_space import (Berkovich_Cp_Affine, - Berkovich_Cp_Projective, is_Berkovich_Cp, Berkovich_Element_Cp) + Berkovich_Cp_Projective, is_Berkovich_Cp, + Berkovich_Element_Cp_Affine, Berkovich_Element_Cp_Projective) from sage.rings.padics.factory import Qp from sage.structure.element import get_coercion_model from sage.schemes.projective.projective_space import ProjectiveSpace @@ -31,6 +32,7 @@ from sage.rings.padics.padic_base_generic import pAdicBaseGeneric from sage.dynamics.arithmetic_dynamics.projective_ds import DynamicalSystem_projective from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine +from sage.rings.rational_field import QQ @add_metaclass(InheritComparisonClasscallMetaclass) class DynamicalSystem_Berkovich(Element): @@ -97,6 +99,16 @@ class DynamicalSystem_Berkovich(Element): Defn: Defined on coordinates by sending (x) to (x^2 + 1 + O(3^20)) + ``domain`` is ignored if a dynamical system or an endomorphism is + passsed in, unless that morphism is not defined over a padic ring/field:: + + sage: f = DynamicalSystem_affine(x^2+1) + sage: C = Berkovich_Cp_Projective(3) + sage: DynamicalSystem_Berkovich(f, C) + Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x) to + (x^2 + 1 + O(3^20)) + Creating a map on Berkovich space creates the Berkovich space it acts on:: @@ -129,12 +141,14 @@ def __classcall_private__(cls, dynamical_system, domain=None): """ if not(domain is None or is_Berkovich_Cp(domain)): raise ValueError('domain must be a Berkovich space over Cp') + morphism_domain = None if isinstance(dynamical_system, SchemeMorphism_polynomial): morphism_domain = dynamical_system.domain() if not domain is None: if isinstance(domain, Berkovich_Cp_Affine): return DynamicalSystem_Berkovich_affine(dynamical_system,domain) + if not morphism_domain is None: from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine if is_AffineSpace(morphism_domain) or isinstance(domain, AlgebraicScheme_subscheme_affine): return DynamicalSystem_Berkovich_affine(dynamical_system,domain) @@ -173,32 +187,6 @@ def domain(self): """ return self._domain - def __call__(self, x): - """ - Makes dynamical systems on Berkovich space over ``Cp`` callable. - - INPUT: - - - ``x`` -- a point of Berkovich space over ``Cp`` - - EXAMPLES:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2, y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: B = g.domain() - sage: Q1 = B(2) - sage: g(Q1) - Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) - """ - if not isinstance(x.parent(), type(self._domain)): - raise ValueError('action of dynamical system not defined on %s' %x.parent()) - if x.type_of_point() == 1: - return self.domain()(self._system(x.center())) - if x.type_of_point() == 4: - raise NotImplementedError('action on Type IV points not implemented') - #TODO write a better check for zeros in disk - def defining_polynomials(self): """ Return the defining polynomials. @@ -311,8 +299,11 @@ def __classcall_private__(cls, dynamical_system, domain=None): """ Returns the approapriate dynamical system on projective Berkovich space over ``Cp``. """ - if not isinstance(dynamical_system, DynamicalSystem_projective): - dynamical_system = DynamicalSystem_projective(dynamical_system) + if not isinstance(dynamical_system, DynamicalSystem): + if not isinstance(dynamical_system, DynamicalSystem_projective): + dynamical_system = DynamicalSystem_projective(dynamical_system) + else: + raise ValueError('affine dynamical system passed to projective constructor') R = dynamical_system.base_ring() morphism_domain = dynamical_system.domain() if not isinstance(R, pAdicBaseGeneric): @@ -341,6 +332,89 @@ def __init__(self, dynamical_system, domain=None): """ DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) + def dehomogenize(self, n): + """ + Returns the map induced by the standard dehomogenization. + + The dehomogenization is done at the ``n[0]`` coordinate + of the domain and the ``n[1]`` coordinate of the codomain. + + INPUT: + + - ``n`` -- a tuple of nonnegative integers; if ``n`` is an integer, + then the two values of the tuple are assumed to be the same + + OUTPUT: A dynamical system on affine Berkovich space + + EXAMPLES:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2 + y^2, x*y + y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: g.dehomogenize(1) + Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x) to + ((x^2 + 1 + O(3^20))/(x + 1 + O(3^20))) + """ + new_system = self._system.dehomogenize(n) + base_ring = self.domain().base_ring().base_ring() #2 base rings since this is projective berkovich space + new_domain = Berkovich_Cp_Affine(base_ring) + return DynamicalSystem_Berkovich_affine(new_system,new_domain) + + def __call__(self, x): + """ + Makes dynamical systems on Berkovich space over ``Cp`` callable. + + INPUT: + + - ``x`` -- a point of Berkovich space over ``Cp`` + + EXAMPLES:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2, y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: B = g.domain() + sage: Q1 = B(2) + sage: g(Q1) + Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) + """ + if not isinstance(x.parent(), type(self._domain)): + try: + x = self.domain()(x) + except: + raise ValueError('action of dynamical system not defined on %s' %x.parent()) + if x.type_of_point() == 1: + return self.domain()(self._system(x.center())) + if x.type_of_point() == 4: + raise NotImplementedError('action on Type IV points not implemented') + f = self._system + """ + if x.type_of_point() == 2: + from sage.matrix.constructor import Matrix + from sage.modules.free_module_element import vector + M = Matrix([[1,2],[0,1]]) + X = M * vector(f[0].parent().gens()) + F = vector(f._polys) + F = list(F(list(X))) + """ + #TODO write a better check for zeros in disk + P = f.domain() + if P.gens()[0] in f.defining_polynomials()[1].variables(): + raise ValueError('action on Type II/III points only defined for polynomials') + nth_derivative = f.dehomogenize(1).defining_polynomials()[0] + variable = nth_derivative.parent().gens()[0] + a = x.center()[0] + Taylor_expansion = [] + from sage.functions.other import factorial + for i in range(f.degree()+1): + Taylor_expansion.append(nth_derivative(a)*1/factorial(i)) + nth_derivative = nth_derivative.derivative(variable) + r = x.radius() + new_center = f(a) + new_radius = max([Taylor_expansion[i].abs()*r**i for i in range(1,len(Taylor_expansion))]) + return self.domain()(new_center, new_radius) + class DynamicalSystem_Berkovich_affine(DynamicalSystem_Berkovich): @staticmethod def __classcall_private__(cls, dynamical_system, domain=None): @@ -372,4 +446,57 @@ def __init__(self, dynamical_system, domain): """ Python constructor. """ - DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) \ No newline at end of file + DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) + + def homogenize(self, n): + """ + Returns the homogenization of this dynamical system. + + For dynamical systems on Berkovich space, this is the dynamical + system on projective space induced by the homogenization of + the dynamical system. + + INPUT: + + - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, + then the two values of the tuple are assumed to be the same + + OUTPUT: a dynamical system on projective Berkovich space + + EXAMPLES:: + + sage: A. = AffineSpace(Qp(3),1) + sage: f = DynamicalSystem_affine(1/x) + sage: f = DynamicalSystem_Berkovich(f) + sage: f.homogenize(1) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x0 : x1) to + (x1 : x0) + + """ + new_system = self._system.homogenize(n) + base_ring = self.domain().base_ring() + new_domain = Berkovich_Cp_Affine(base_ring) + return DynamicalSystem_Berkovich_projective(new_system,new_domain) + + def __call__(self, x): + """ + Makes this dynamical system callable. + + EXAMPLES:: + + sage: P. = AffineSpace(Qp(3),1) + sage: f = DynamicalSystem_affine(x^2) + sage: g = DynamicalSystem_Berkovich(f) + sage: B = g.domain() + sage: Q1 = B(2) + sage: g(Q1) + Type I point centered at 1 + 3 + O(3^20) + """ + if not isinstance(x, Berkovich_Element_Cp_Affine): + try: + x = self.domain()(x) + except: + raise ValueError('action of dynamical system not defined on %s' %x) + proj_system = self.homogenize(1) + return self.domain()(proj_system(x)) \ No newline at end of file From af5a330016c966a668bdb07f5ef1a44ec4123dcf Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 29 Jun 2020 10:47:58 +0200 Subject: [PATCH 028/379] use _random_nonzero_element --- src/sage/coding/grs_code.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index a0c663c1447..a17c2b88dc2 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -441,14 +441,8 @@ def dual_code(self): EXAMPLES:: - sage: def rand_non_zero(F): - ....: x = F.random_element() - ....: while x == 0: - ....: x = F.random_element() - ....: return x - sage: F = GF(59) - sage: colmults = [ rand_non_zero(F) for i in range(40) ] + sage: colmults = [ F._random_nonzero_element() for i in range(40) ] sage: C = codes.GeneralizedReedSolomonCode(F.list()[:40], 12, colmults) sage: Cd = C.dual_code(); Cd [40, 28, 13] Generalized Reed-Solomon Code over GF(59) From 5434d5424eb9c3abee0bf37f1f654083b1f99c87 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 29 Jun 2020 15:02:10 -0400 Subject: [PATCH 029/379] 29949: preliminary image of Type II points + constructor funcationality --- .../arithmetic_dynamics/berkovich_ds.py | 53 +++++++++++++++++-- .../arithmetic_dynamics/generic_ds.py | 4 ++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 28765ae080a..2566ebbe800 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -389,19 +389,64 @@ def __call__(self, x): if x.type_of_point() == 4: raise NotImplementedError('action on Type IV points not implemented') f = self._system - """ if x.type_of_point() == 2: from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector - M = Matrix([[1,2],[0,1]]) + y,w = f.domain().gens()[0],f.domain().gens()[1] + field = f.domain().base_ring() + M = Matrix([[field(x.prime()**(-1*x.power())),x.center()[0]],[field(0),field(1)]]) X = M * vector(f[0].parent().gens()) F = vector(f._polys) F = list(F(list(X))) - """ + R = field['z'] + z = R.gens()[0] + for i in range(len(F)): + F[i] = F[i].subs({y:z,w:1}) + print(F) + lcm = field(1) + for poly in F: + for i in poly: + if i != 0: + lcm = i.denominator().lcm(lcm) + for i in range(len(F)): + F[i] *= lcm + gcd = [i for i in F[0] if i != 0][0] + for poly in F: + for i in poly: + if i != 0: + gcd = gcd = gcd*i*gcd.lcm(i).inverse_of_unit() + for i in range(len(F)): + F[i] *= gcd.inverse_of_unit() + gcd = F[0].gcd(F[1]) + F[0] = F[0].quo_rem(gcd)[0] + F[1] = F[1].quo_rem(gcd)[0] + print('num = ', F[0]) + print('dem = ', F[1]) + fraction = [] + for poly in F: + new_poly = [] + for i in poly: + new_poly.append((i).residue()) + new_poly = R(new_poly) + fraction.append((new_poly)) + gcd = fraction[0].gcd(fraction[1]) + num = fraction[0].quo_rem(gcd)[0] + dem = fraction[1].quo_rem(gcd)[0] + print('num = ', num) + print('dem = ', dem) + #if the reduction is not constant, the image + #is the Gauss point + if not(num.is_constant() and dem.is_constant()): + return self.domain()(QQ(0),QQ(1)) + reduced_value = field(num*dem.inverse_of_unit()).lift_to_precision(field.precision_cap()) + new_num = F[0]-reduced_value*F[1] + power_of_p = min([i.valuation() for i in new_num]) + inverse_map = (field(x.prime()**power_of_p)*z + reduced_value) + return self.domain()(inverse_map(0),(inverse_map(1)-inverse_map(0)).abs()) #TODO write a better check for zeros in disk P = f.domain() if P.gens()[0] in f.defining_polynomials()[1].variables(): - raise ValueError('action on Type II/III points only defined for polynomials') + raise ValueError('action on Type II/III points only implemented for polynomials') nth_derivative = f.dehomogenize(1).defining_polynomials()[0] variable = nth_derivative.parent().gens()[0] a = x.center()[0] diff --git a/src/sage/dynamics/arithmetic_dynamics/generic_ds.py b/src/sage/dynamics/arithmetic_dynamics/generic_ds.py index 10cd80b6da5..0825e414f51 100644 --- a/src/sage/dynamics/arithmetic_dynamics/generic_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/generic_ds.py @@ -36,6 +36,7 @@ class initialization directly. from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField_generic from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.qqbar import AlgebraicField_common +from sage.schemes.berkovich.berkovich_space import is_Berkovich_Cp from sage.rings.rational_field import QQ from copy import copy @@ -164,6 +165,9 @@ def __classcall_private__(cls, morphism_or_polys, domain=None, names=None): if is_AffineSpace(domain) or isinstance(domain, AlgebraicScheme_subscheme_affine): from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine return DynamicalSystem_affine(morphism_or_polys, domain) + if is_Berkovich_Cp(domain): + from sage.dynamics.arithmetic_dynamics.berkovich_ds import DynamicalSystem_Berkovich + return DynamicalSystem_Berkovich(morphism_or_polys,domain) from sage.dynamics.arithmetic_dynamics.projective_ds import DynamicalSystem_projective return DynamicalSystem_projective(morphism_or_polys, domain, names) From 016f058c56d27555e0c09bf400d025db1ed27c44 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 29 Jun 2020 18:12:07 -0400 Subject: [PATCH 030/379] 29949: added image of Type II for all cases --- src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 2566ebbe800..932229da530 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -414,14 +414,12 @@ def __call__(self, x): for poly in F: for i in poly: if i != 0: - gcd = gcd = gcd*i*gcd.lcm(i).inverse_of_unit() + gcd = gcd*i*gcd.lcm(i).inverse_of_unit() for i in range(len(F)): F[i] *= gcd.inverse_of_unit() gcd = F[0].gcd(F[1]) F[0] = F[0].quo_rem(gcd)[0] F[1] = F[1].quo_rem(gcd)[0] - print('num = ', F[0]) - print('dem = ', F[1]) fraction = [] for poly in F: new_poly = [] @@ -432,8 +430,11 @@ def __call__(self, x): gcd = fraction[0].gcd(fraction[1]) num = fraction[0].quo_rem(gcd)[0] dem = fraction[1].quo_rem(gcd)[0] - print('num = ', num) - print('dem = ', dem) + if dem.is_zero(): + f = DynamicalSystem_affine(F[0]/F[1]).homogenize(1) + f = f.conjugate(Matrix([[0, 1], [1 , 0]])) + g = DynamicalSystem_Berkovich(f) + return g(self.domain()(QQ(0),QQ(1))).involution_map() #if the reduction is not constant, the image #is the Gauss point if not(num.is_constant() and dem.is_constant()): From 7f8ed922bbbf76770e3e5c7d6e900fca8ea2fed2 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 30 Jun 2020 10:02:26 -0400 Subject: [PATCH 031/379] 29949: more examples --- .../arithmetic_dynamics/berkovich_ds.py | 103 +++++++++++++++--- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 932229da530..4f529da9c30 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -1,8 +1,8 @@ r""" -Dynamical systmes on Berkovich space over \CC_p. +Dynamical systmes on Berkovich space over `\CC_p`. -A dynamical system on Berkovich space over \CC_p is -determined by a dynamical system on A^1(\CC_p) or P^1(\CC_p), +A dynamical system on Berkovich space over `\CC_p` is +determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, which naturally induces a dynamical system on affine or projective Berkovich space. """ @@ -238,17 +238,17 @@ class DynamicalSystem_Berkovich_projective(DynamicalSystem_Berkovich): INPUT:: - ``dynamical_system`` -- any input which defines a dynamical - system over affine or projective space, or a dynamical system - over affine or projective space. If the input is a list - of homogenous polynomials, then ``domain`` is taken to - be projective Berkovich space, unless specified. - If this input is not defined over a padic field, - then ``domain`` MUST be specified. + system over affine or projective space, or a dynamical system + over affine or projective space. If the input is a list + of homogenous polynomials, then ``domain`` is taken to + be projective Berkovich space, unless specified. + If this input is not defined over a padic field, + then ``domain`` MUST be specified. - ``domain`` -- (optional) affine or projective Berkovich space - over `\CC_p`. If the input to ``dynamical_system`` is - not defined over `\QQ_p` or a finite extension, ``domain`` - must be specified. + over `\CC_p`. If the input to ``dynamical_system`` is + not defined over `\QQ_p` or a finite extension, ``domain`` + must be specified. EXAMPLES: @@ -369,15 +369,34 @@ def __call__(self, x): - ``x`` -- a point of Berkovich space over ``Cp`` - EXAMPLES:: + EXAMPLES: + + The image of Type I point is the image of the center:: sage: P. = ProjectiveSpace(Qp(3),1) sage: f = DynamicalSystem_projective([x^2, y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: B = g.domain() + sage: F = DynamicalSystem_Berkovich(f) + sage: B = F.domain() sage: Q1 = B(2) - sage: g(Q1) + sage: F(Q1) Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) + + For Type II/III points with no poles in the corresponding disk, + the image is the Type II/III point corresponding to the image + of the disk:: + + sage: Q2 = B(1,3) + sage: F(Q2) + Type II point centered at (0 : 1 + O(3^20)) of radius 3^2 + + The image of Type II points can be computed even when there + are poles in the disk:: + + sage: g = DynamicalSystem_projective([x^2 + y^2, x*y]) + sage: G = DynamicalSystem_Berkovich(f) + sage: Q3 = B(0,1) + sage: G(Q3) + Type II point centered at (0 : 1 + O(3^20)) of radius 3^0 """ if not isinstance(x.parent(), type(self._domain)): try: @@ -389,6 +408,8 @@ def __call__(self, x): if x.type_of_point() == 4: raise NotImplementedError('action on Type IV points not implemented') f = self._system + #for Type II points, we use the approach outlined in example 7.37 in + #'Dynamics in One Non-Archimedean Variable' by Benedetto if x.type_of_point() == 2: from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector @@ -402,7 +423,6 @@ def __call__(self, x): z = R.gens()[0] for i in range(len(F)): F[i] = F[i].subs({y:z,w:1}) - print(F) lcm = field(1) for poly in F: for i in poly: @@ -447,7 +467,7 @@ def __call__(self, x): #TODO write a better check for zeros in disk P = f.domain() if P.gens()[0] in f.defining_polynomials()[1].variables(): - raise ValueError('action on Type II/III points only implemented for polynomials') + raise ValueError('action on Type III points only implemented for polynomials') nth_derivative = f.dehomogenize(1).defining_polynomials()[0] variable = nth_derivative.parent().gens()[0] a = x.center()[0] @@ -462,6 +482,53 @@ def __call__(self, x): return self.domain()(new_center, new_radius) class DynamicalSystem_Berkovich_affine(DynamicalSystem_Berkovich): + r""" + A dynamical system of the affine Berkovich line over `\CC_p`. + + INPUT: + + - ``dynamical_system`` -- any input which defines a dynamical + system over affine or projective space, or a dynamical system + over affine or projective space. If the input is a list + of homogenous polynomials, then ``domain`` is taken to + be projective Berkovich space, unless specified. + If this input is not defined over a padic field, + then ``domain`` MUST be specified. + + - ``domain`` -- (optional) affine or projective Berkovich space + over `\CC_p`. If the input to ``dynamical_system`` is + not defined over `\QQ_p` or a finite extension, ``domain`` + must be specified. + + EXAMPLES: + + A dynamical system of the affine Berkovich line is + induced by a dynamical system on `\QQ_p` or an extension + of `\QQ_p`:: + + sage: A. = AffineSpace(Qp(5), 1) + sage: f = DynamicalSystem_affine([(x^2+1)/x]) + sage: DynamicalSystem_Berkovich_affine(f) + Dynamical system of Affine Berkovich line over Cp(5) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x) to + ((x^2 + 1 + O(5^20))/x) + + Dynamical systems can also be created directly from polynomials:: + + sage: DynamicalSystem_Berkovich_affine([(x^2+1)/x]) + Dynamical system of Affine Berkovich line over Cp(5) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x) to + ((x^2 + 1 + O(5^20))/x) + + Or from a morphism:: + + sage: H = End(A) + sage: phi = H([x+3]) + sage: DynamicalSystem_Berkovich_affine(phi) + Dynamical system of Affine Berkovich line over Cp(5) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x) to + (x + 3 + O(5^20)) + """ @staticmethod def __classcall_private__(cls, dynamical_system, domain=None): if not isinstance(dynamical_system, DynamicalSystem_affine): From 91d397c95f019b065912a82215058d5ad9e42802 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 30 Jun 2020 15:18:07 -0400 Subject: [PATCH 032/379] 29949: added conjugate, temporarily commented scale_by --- .../arithmetic_dynamics/berkovich_ds.py | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py index 4f529da9c30..b0d7319ae7e 100644 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py @@ -187,6 +187,28 @@ def domain(self): """ return self._domain + def __getitem(self, i): + """ + Returns the ith polynomial. + + INPUT: + + - ``i`` -- an integer + + OUTPUT: + + - element of polynomial ring or a fraction field of a polynomial ring + + EXAMPLES:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2 + y^2, 2*y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: g[0] + x^2 + y^2 + """ + return self._polys[i] + def defining_polynomials(self): """ Return the defining polynomials. @@ -332,6 +354,61 @@ def __init__(self, dynamical_system, domain=None): """ DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) + """ + def scale_by(self, t): + "" + Scales each coordinate by a factor of ``t``. + + INPUT: + + - ``t`` -- a ring element. + + OUTPUT: + + - None. + "" + field = self.domain().base_ring() + R = field['z'] + x,y = self._polys[0].variables()[0], self._polys[0].variables()[1] + z = R.gens()[0] + old_polys = self._polys[:] + for i in range(len(old_polys)): + old_polys[i] = old_polys[i].subs({x:z,}) + new_polys = [] + for i in self: + new_polys.append(i*t.numerator()) + """ + + def conjugate(self, M, adjugate=False): + r""" + Conjugate this dynamical system by ``M``, i.e. `M^{-1} \circ f \circ M`. + + If possible the new map will be defined over the same space. + Otherwise, will try to coerce to the base ring of ``M``. + + INPUT: + + - ``M`` -- a square invertible matrix + + - ``adjugate`` -- (default: ``False``) boolean, also classically + called adjoint, takes a square matrix ``M`` and finds the transpose + of its cofactor matrix. Used for conjugation in place of inverse + when specified ``'True'``. Functionality is the same in projective space. + + OUTPUT: a dynamical system + + EXAMPLES:: + + sage: P. = ProjectiveSpace(Qp(3),1) + sage: f = DynamicalSystem_projective([x^2 + y^2, 2*y^2]) + sage: g = DynamicalSystem_Berkovich(f) + sage: g.conjugate(Matrix([[1,1],[0,1]])) + Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map + Defn: Defined on coordinates by sending (x : y) to + (x^2 + (2 + O(3^20))*x*y : (2 + O(3^20))*y^2) + """ + return DynamicalSystem_Berkovich(self._system.conjugate(M,adjugate)) + def dehomogenize(self, n): """ Returns the map induced by the standard dehomogenization. From 529772071577bc817abd44fb9dcaaf61cd697460 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Tue, 30 Jun 2020 16:07:35 -0300 Subject: [PATCH 033/379] Implemented Lie Conformal Algebras --- src/doc/en/reference/algebras/index.rst | 1 + .../algebras/lie_conformal_algebras.rst | 18 + src/doc/en/reference/categories/index.rst | 1 + src/doc/en/reference/references/index.rst | 9 + src/sage/algebras/all.py | 1 + .../lie_conformal_algebras/__init__.py | 0 .../algebras/lie_conformal_algebras/all.py | 18 + .../lie_conformal_algebras/examples.py | 258 +++++ ...initely_generated_lie_conformal_algebra.py | 124 +++ .../graded_lie_conformal_algebra.py | 121 +++ .../lie_conformal_algebra.py | 341 +++++++ .../lie_conformal_algebra_element.py | 324 ++++++ .../lie_conformal_algebra_with_basis.py | 63 ++ .../lie_conformal_algebra_with_generators.py | 97 ++ ..._conformal_algebra_with_structure_coefs.py | 309 ++++++ src/sage/categories/all.py | 3 + src/sage/categories/category_with_axiom.py | 6 +- src/sage/categories/lie_conformal_algebras.py | 940 ++++++++++++++++++ 18 files changed, 2633 insertions(+), 1 deletion(-) create mode 100644 src/doc/en/reference/algebras/lie_conformal_algebras.rst create mode 100644 src/sage/algebras/lie_conformal_algebras/__init__.py create mode 100644 src/sage/algebras/lie_conformal_algebras/all.py create mode 100644 src/sage/algebras/lie_conformal_algebras/examples.py create mode 100644 src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py create mode 100644 src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py create mode 100644 src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py create mode 100644 src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py create mode 100644 src/sage/categories/lie_conformal_algebras.py diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 2a7c43950da..1a415c7c337 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -97,6 +97,7 @@ Non-associative algebras :maxdepth: 2 lie_algebras + lie_conformal_algebras sage/algebras/jordan_algebra sage/combinat/free_dendriform_algebra sage/combinat/free_prelie_algebra diff --git a/src/doc/en/reference/algebras/lie_conformal_algebras.rst b/src/doc/en/reference/algebras/lie_conformal_algebras.rst new file mode 100644 index 00000000000..357f08a33e8 --- /dev/null +++ b/src/doc/en/reference/algebras/lie_conformal_algebras.rst @@ -0,0 +1,18 @@ +Lie Conformal Algebras +====================== + +.. toctree:: + + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/examples + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element + ../sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs + + +.. SEEALSO:: + + :mod:`The Category of Lie Conformal Algebras` diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index c9ae6fcf673..83771654981 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -130,6 +130,7 @@ Individual Categories sage/categories/left_modules sage/categories/lie_algebras sage/categories/lie_algebras_with_basis + sage/categories/lie_conformal_algebras sage/categories/lie_groups sage/categories/loop_crystals sage/categories/l_trivial_semigroups diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index f76b818008f..e7fc7fecc14 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1912,6 +1912,9 @@ REFERENCES: *Differential Geometry of Lightlike Submanifolds*, Frontiers in Mathematics, 2010. +.. [DSK2006] \A. De Sole and V. Kac. "Finite vs Affine W-algebras". Jpn. J. Math. + (2006) vol. 1, no. 1, pp 137--261 + .. [Du2001] \I. Duursma, "From weight enumerators to zeta functions", in Discrete Applied Mathematics, vol. 111, no. 1-2, pp. 55-73, 2001. @@ -3052,6 +3055,9 @@ REFERENCES: .. [Ka1990] Victor G. Kac. *Infinite-dimensional Lie Algebras*. Third edition. Cambridge University Press, Cambridge, 1990. +.. [Kac1997] \V. Kac, "Vertex algebras for beginners". Second Edition. vol 10. + university lecture series. AMS, Cambridge, 1997. + .. [Kal1992] \B. Kaliski, *The MD2 message-digest algorithm*; in RFS 1319, (1992). @@ -3522,6 +3528,9 @@ REFERENCES: .. [Li1995b] \P. Littelmann, Paths and root operators in representation theory. Ann. of Math. (2) 142 (1995), no. 3, 499-525. +.. [Li2005] \H. Li, *Abelianizing vertex algebras*. Comm. Math. Phys. vol. 259, + no. 2, pp. 391--411 (2005) + .. [Lic1977] \A. Lichnerowicz, *Les variétés de Poisson et leurs algèbres de Lie associées*, Journal of Differential Geometry **12**, 253 (1977); :doi:`10.4310/jdg/1214433987` diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index 8dd0cdf653f..f1d021503a9 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -32,6 +32,7 @@ from .steenrod.all import * from .lie_algebras.all import * from .quantum_groups.all import * +from .lie_conformal_algebras.all import * from .finite_dimensional_algebras.all import FiniteDimensionalAlgebra diff --git a/src/sage/algebras/lie_conformal_algebras/__init__.py b/src/sage/algebras/lie_conformal_algebras/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/algebras/lie_conformal_algebras/all.py b/src/sage/algebras/lie_conformal_algebras/all.py new file mode 100644 index 00000000000..239f8100e49 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/all.py @@ -0,0 +1,18 @@ +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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.misc.lazy_import import lazy_import +lazy_import('sage.algebras.lie_conformal_algebras.lie_conformal_algebra', +'LieConformalAlgebra') +lazy_import('sage.algebras.lie_conformal_algebras.examples', + ('AffineLieConformalAlgebra', + 'NeveuSchwarzLieConformalAlgebra', + 'VirasoroLieConformalAlgebra')) diff --git a/src/sage/algebras/lie_conformal_algebras/examples.py b/src/sage/algebras/lie_conformal_algebras/examples.py new file mode 100644 index 00000000000..f81381086f6 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/examples.py @@ -0,0 +1,258 @@ +r""" +Examples of Lie Conformal Algebras + +- Affine Lie Conformal Algebra + + The affine Kac-Moody Lie conformal algebra associated to the + finite dimensional simple Lie algebra `\mathfrak{g}`. For a commutative + ring `R`, it is the `R[T]`-module freely generated by `\mathfrak{g}` + plus a central element `K` satisfying `TK = 0`. The non-vanishing + `\lambda`-brackets are given by + + .. MATH:: + + [a_\lambda b] = [a,b] + \lambda (a,b)K, + + where `a,b \in \mathfrak{g}` and `(a,b)` is the normalized + form of `\mathfrak{g}` so that its longest root has square-norm + `2`. + +- Virasoro Lie Conformal Algebra + + The Virasoro Lie conformal algebra is generated by `L` and a central + element `C`. The `\lambda`-brackets are given by: + + .. MATH:: + + [L_\lambda L] = T L + 2 \lambda L + \frac{\lambda^3}{12} C. + + It is an H-graded Lie conformal algebra with `L` of degree `2`. + +- Neveu-Schwarz Super Lie Conformal Algebra + + The `N=1` or *Neveu-Schwarz* super Lie conformal algebra is a super + extension of the Virasoro Lie conformal algebra with generators `L` + and `C` by an odd primary generator `G` of conformal weight `3/2`. The + remaining `\lambda`-bracket is given by: + + .. MATH:: + + [G_\lambda G] = 2L + \frac{\lambda^2}{3} C. + +AUTHORS: + +- Reimundo Heluani (2020-06-15): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra +from sage.rings.integer import Integer +from sage.algebras.lie_algebras.lie_algebra import LieAlgebra + +class AffineLieConformalAlgebra(GradedLieConformalAlgebra): + def __init__(self, R, ct, names=None, prefix=None, bracket=None): + r""" + The current or affine Kac-Moody Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative Ring; the base ring for this Lie + conformal algebra. + - ``ct`` -- a ``str`` or a :mod:`CartanType`; the Cartan Type for + the corresponding finite dimensional Lie algebra. It must + correspond to a simple finite dimensional Lie algebra. + - ``names`` -- a list of ``str`` or ``None`` (default: ``None``) + ; alternative names for the generators. If ``None`` the + generators are labeled by the corresponding root and coroot + vectors. + - ``prefix`` -- a ``str``; parameter passed to + :class:`IndexedGenerators<\ + sage.structure.indexed_generators.IndexedGenerators>` + - ``bracket`` -- a ``str``; parameter passed to + :class:`IndexedGenerators<\ + sage.structure.indexed_generators.IndexedGenerators>`. + + EXAMPLES:: + + sage: R = AffineLieConformalAlgebra(QQ, 'A1') + sage: R + The affine Lie conformal algebra of type ['A', 1] over Rational Field + sage: R.an_element() + B[alpha[1]] + B[alphacheck[1]] + B[-alpha[1]] + B['K'] + + sage: R = AffineLieConformalAlgebra(QQ, 'A1', names = ('e', 'h','f')) + sage: R.inject_variables() + Defining e, h, f, K + sage: Family(e.bracket(f.T(3))) + Finite family {0: 6*T^(3)h, 1: 6*T^(2)h, 2: 6*Th, 3: 6*h, 4: 24*K} + + sage: V = AffineLieConformalAlgebra(QQ, CartanType(["A",2,1])) + Traceback (most recent call last): + ... + ValueError: Only affine algebras of simple finite dimensionalLie algebras are implemented + + OUTPUT: + + The Affine Lie conformal algebra associated with the finite + dimensional simple Lie algebra of Cartan type ``ct``. + """ + if type(ct) is str: + from sage.combinat.root_system.cartan_type import CartanType + try: + ct = CartanType(ct) + except IndexError: + raise ValueError("ct must be a valid Cartan Type") + if not (ct.is_finite() and ct.is_irreducible ): + raise ValueError("Only affine algebras of simple finite dimensional" + "Lie algebras are implemented") + hv = Integer(ct.dual_coxeter_number()) + g = LieAlgebra(R, cartan_type=ct) + B = g.basis() + S = B.keys() + gdict = {} + for k1 in S: + for k2 in S: + if S.rank(k2) <= S.rank(k1): + myb = B[k1].bracket(B[k2]).monomial_coefficients() + myf = R(2).inverse_of_unit()*R(hv).inverse_of_unit()\ + *g.killing_form(B[k1],B[k2]) + if myb or myf: + gdict[(k1,k2)] = {} + if myb: + gdict[(k1,k2)][0] = {(nk,0):v for nk,v in \ + myb.items()} + if myf: + gdict[(k1,k2)][1] = {('K',0):myf} + + weights = (1,)*B.cardinality() + self._ct = ct + if prefix is None and names is None: + prefix = 'B' + + GradedLieConformalAlgebra.__init__(self, + R, gdict, index_set=S, + central_elements=('K',), weights=weights, + names=names, prefix=prefix,bracket=bracket) + + def cartan_type(self): + """ + The Cartan type of this Lie conformal algbera. + + EXAMPLES:: + + sage: R = AffineLieConformalAlgebra(QQ, 'B3') + sage: R + The affine Lie conformal algebra of type ['B', 3] over Rational Field + sage: R.cartan_type() + ['B', 3] + """ + return self._ct + + def _repr_(self): + """ + The name of this Lie conformal algebra. + """ + return "The affine Lie conformal algebra of type {} over {}".format( + self._ct,self.base_ring()) + +class NeveuSchwarzLieConformalAlgebra(GradedLieConformalAlgebra): + + def __init__(self, R): + """ + The Neveu-Schwarz super Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative Ring; the base ring of this Lie + conformal algebra. + + EXAMPLES:: + + sage: R = NeveuSchwarzLieConformalAlgebra(AA); R + The Neveu-Schwarz super Lie conformal algebra over Algebraic Real Field + sage: R.structure_coefficients() + Finite family {('G', 'G'): ((0, 2*L), (2, 2/3*C)), ('G', 'L'): ((0, 1/2*TG), (1, 3/2*G)), ('L', 'G'): ((0, TG), (1, 3/2*G)), ('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} + sage: R.inject_variables() + Defining L, G, C + sage: G.nproduct(G,0) + 2*L + sage: G.degree() + 3/2 + """ + nsdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, + 3:{('C', 0):R(2).inverse_of_unit()}}, + ('L','G'):{0:{('G',1):1}, 1:{('G',0):R(3)*R(2).\ + inverse_of_unit()}}, ('G','G'): {0:{('L',0):2}, + 2:{('C',0):R(2)*R(3).inverse_of_unit()}}} + from sage.rings.rational_field import QQ + weights = (2,QQ(3/2)) + parity = (0,1) + GradedLieConformalAlgebra.__init__(self, R, nsdict, names=('L','G'), + central_elements=('C',), weights=weights, parity=parity) + + def _repr_(self): + """ + The name of this Lie Conformal algebra. + + EXAMPLES:: + + sage: R = NeveuSchwarzLieConformalAlgebra(GF(5)); R + The Neveu-Schwarz super Lie conformal algebra over Finite Field of size 5 + """ + return "The Neveu-Schwarz super Lie conformal algebra over {}".\ + format(self.base_ring()) + + + +class VirasoroLieConformalAlgebra(GradedLieConformalAlgebra): + def __init__(self, R): + """ + The Virasoro Lie Conformal algebra over `R`. + + INPUT: + + - ``R`` -- a commutative ring; behaviour is undefined if `R` is + not a Field of characteristic zero. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: Vir.inject_variables() + Defining L, C + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + + TESTS:: + + sage: Vir.gens() + (L, C) + """ + virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, + 3:{('C', 0):R(2).inverse_of_unit()}}} + GradedLieConformalAlgebra.__init__(self,R, virdict, + names = ('L',), central_elements = ('C',), weights = (2,)) + + def _repr_(self): + """ + The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: VirasoroLieConformalAlgebra(QQbar) + The Virasoro Lie conformal algebra over Algebraic Field + """ + return "The Virasoro Lie conformal algebra over {}".format( + self.base_ring()) + diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py new file mode 100644 index 00000000000..872754cf816 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py @@ -0,0 +1,124 @@ +""" +Finitely Generated Lie Conformal Algebras. + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.misc.cachefunc import cached_method +from sage.categories.lie_conformal_algebras import LieConformalAlgebras +from .lie_conformal_algebra_with_generators import \ + LieConformalAlgebraWithGenerators + +class FinitelyGeneratedLieConformalAlgebra(LieConformalAlgebraWithGenerators): + def __init__(self, R, index_set=None, central_elements=None, category=None, + element_class=None, prefix=None, names=None, latex_names=None, + **kwds): + """ + Abstract base class for finitely generated Lie conformal + algebras. + + This class provides minimal functionality, simply sets the + number of generators. + """ + category = LieConformalAlgebras(R).FinitelyGenerated().or_subcategory( + category) + + from sage.categories.sets_cat import Sets + if index_set not in Sets().Finite(): + raise TypeError("index_set must be a finite set") + + super(FinitelyGeneratedLieConformalAlgebra,self).__init__(R, + index_set=index_set, central_elements=central_elements, + category=category, element_class=element_class, + prefix=prefix, **kwds) + self._ngens = len(self._generators) + self._names = names + self._latex_names = latex_names + + def _repr_(self): + """ + The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: bosondict = {('a','a'):{1:{('K',0):1}}} + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',),central_elements=('K',)) + sage: R + Lie conformal algebra with generators (a, K) over Rational Field + """ + if self._ngens == 1: + return "Lie conformal algebra generated by {0} over {1}".format( + self.gen(0), self.base_ring()) + return "Lie conformal algebra with generators {0} over {1}".format( + self.gens(), self.base_ring()) + def _an_element_(self): + """ + An element of this Lie conformal algebra. + + EXAMPLES:: + + sage: R = NeveuSchwarzLieConformalAlgebra(QQ); R.an_element() + L + G + C + """ + return self.sum(self.gens()) + + def ngens(self): + """ + The number of generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); Vir.ngens() + 2 + sage: V = AffineLieConformalAlgebra(QQ, 'A1'); V.ngens() + 4 + """ + return self._ngens + + @cached_method + def gens(self): + """ + The generators for this Lie conformal algebra. + + OUTPUT: + + This method returns a tuple with the (finite) generators + of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); + sage: Vir.gens() + (L, C) + + .. SEEALSO:: + + :meth:`lie_conformal_algebra_generators<\ + LieConformalAlgebraWithGenerators.\ + lie_conformal_algebra_generators>` + """ + return self.lie_conformal_algebra_generators() + + @cached_method + def central_elements(self): + """ + The central elements of this Lie conformal algebra. + + EXAMPLES:: + + sage: R = NeveuSchwarzLieConformalAlgebra(QQ); R.central_elements() + (C,) + """ + return tuple(LieConformalAlgebraWithGenerators.central_elements(self)) diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py new file mode 100644 index 00000000000..011152faedd --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -0,0 +1,121 @@ +r""" +Graded Lie Conformal Algebras + +A (super) Lie conformal algebra `V` is called `H`-graded if there +exists a decomposition `V = \oplus_n V_n` such that the `\lambda`- +bracket is graded of degree `-1`, that is for homogeneous elements +`a \in V_p`, `b \in V_q` with `\lambda`-brackets: + +.. MATH:: + + [a_\lambda b] = \sum \frac{\lambda^n}{n!} c_n, + +we have `c_n \in V_{p+q-n-1}`. This situation arises typically when `V` +has a vector `L \in V` that generates the Virasoro Lie conformal +algebra. Such that for every `a \in V` we have + +.. MATH:: + + [L_\lambda a] = Ta + \lambda \Delta_a a + O(\lambda^2). + +In this situation `V` is graded by the eigenvalues `\Delta_a` of +`L_{(1)}`, the `(1)`-th product with `L`. When the higher order terms +`O(\lambda^2)` vanish we say that `a` is a *primary vector* of +*conformal weight* or degree `\Delta_a`. + +.. NOTE:: + + Although arbitrary gradings are allowed, many of the constructions + we implement in these classes work only for positive rational + gradings. + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.lie_conformal_algebras import LieConformalAlgebras +from .lie_conformal_algebra_element import GradedLCAElement +from .lie_conformal_algebra_with_structure_coefs import \ + LieConformalAlgebraWithStructureCoefficients + +class GradedLieConformalAlgebra(LieConformalAlgebraWithStructureCoefficients): + def __init__(self, R, s_coeff, index_set=None, central_elements=None, + category=None, prefix=None, names=None, latex_names=None, + parity=None, weights=None, **kwds): + r""" + An H-Graded Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring (default: ``None``); the base + ring of this Lie conformal algebra. Behaviour is undefined if + it is not a field of characteristic zero + + - ``s_coeff`` -- a dictionary (default: ``None``); as in the + input of :class:`LieConformalAlgebra` + + - ``names`` -- tuple of ``str`` (default: ``None``); as in the + input of :class:`LieConformalAlgebra` + + - ``central_elements`` -- tuple of ``str`` (default: ``None``); + as in the input of :class:`LieConformalAlgebra` + + - ``index_set`` -- enumerated set (default: ``None``); as in the + input of :class:`LieConformalAlgebra` + + - ``weights`` -- tuple of non-negative rational numbers + (default: tuple of ``1``); a list of degrees for this Lie + conformal algebra. + This tuple needs to have the same cardinality as + ``index_set`` or ``names``. Central elements are assumed + to have weight ``0``. + + - ``category`` The category that this Lie conformal algebra + belongs to. + + - ``parity`` -- tuple of ``0`` or ``1`` (Default: tuple of + ``0``); a tuple specifying the parity of each non-central + generator. + + EXAMPLES:: + + sage: bosondict = {('a','a'):{1:{('K',0):1}}} + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',),central_elements=('K',), weights=(1,)) + sage: R.inject_variables() + Defining a, K + sage: a.T(3).degree() + 4 + sage: K.degree() + 0 + sage: R.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + """ + category = LieConformalAlgebras(R).Graded().WithBasis()\ + .FinitelyGenerated().or_subcategory(category) + element_class = GradedLCAElement + LieConformalAlgebraWithStructureCoefficients.__init__(self,R, + s_coeff,index_set=index_set,central_elements=central_elements, + category=category, element_class=element_class, prefix=prefix, + names=names, latex_names=latex_names, parity=parity, **kwds) + + if weights is None: + weights = (1,)* (len(self._generators) - + len(self.central_elements())) + if len (weights) != (len(self._generators) - + len(self.central_elements())): + raise ValueError("weights and (non-central) generator lists "\ + "must be of same length") + self._weights = weights diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py new file mode 100644 index 00000000000..a3927cddf5e --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -0,0 +1,341 @@ +r""" +Lie Conformal Algebra + +Let `R` be a commutative ring, a *super Lie conformal algebra* +[Kac1997]_ over `R` +(also known as a *vertex Lie algebra*) is an `R[T]` super module `L` +together with a `\mathbb{Z}/2\mathbb{Z}`-graded `R`-bilinear +operation (called the `\lambda`-bracket) +`L\otimes L \rightarrow L[\lambda]` +(polynomials in `\lambda` with +coefficients in `L`), `a \otimes b \mapsto [a_\lambda b]` satisfying + +1. Sesquilinearity: + + .. MATH:: + + [Ta_\lambda b] = - \lambda [a_\lambda b], \qquad [a_\lambda Tb] = + (\lambda+ T) [a_\lambda b]. + +2. Skew-Symmetry: + + .. MATH:: + + [a_\lambda b] = - (-1)^{p(a)p(b)} [b_{-\lambda - T} a], + + where `p(a)` is `0` if `a` is *even* and `1` if `a` is *odd*. The + bracket in the RHS is computed as follows. First we evaluate + `[b_\mu a]` with the formal + parameter `\mu` to the *left*, then + replace each appearance of the formal variable `\mu` by `-\lambda - T`. + Finally apply `T` to the coefficients in `L`. + +3. Jacobi identity: + + .. MATH:: + + [a_\lambda [b_\mu c]] = [ [a_{\lambda + \mu} b]_\mu c] + + (-1)^{p(a)p(b)} [b_\mu [a_\lambda c ]], + + which is understood as an equality in `L[\lambda,\mu]`. + + `T` is usually called the *translation operation* or the *derivative*. + For an element `a \in L` we will say that `Ta` is the *derivative of* + `a`. We define the *n-th products* `a_{(n)} b` for `a,b \in L` by + + .. MATH:: + + [a_\lambda b] = \sum_{n \geq 0} \frac{\lambda^n}{n!} a_{(n)} b. + + A Lie conformal algebra is called *H-Graded* [DSK2006]_ if there exists + a decomposition `L = \oplus L_n` such that the + `\lambda`-bracket becomes graded of degree `-1`, that is: + + .. MATH:: + + a_{(n)} b \in L_{p + q -n -1} \qquad + a \in L_p, \: b \in L_q, \: n \geq 0. + + In particular this implies that the action of `T` increases + degree by `1`. + +.. NOTE:: + + In the literature arbitrary gradings are allowed. In this + implementation we only support non-negative rational gradings. + + +EXAMPLES: + +1. The **Virasoro** Lie conformal algebra `Vir` over a ring `R` + where `12` is invertible has two generators `L, C` as an `R[T]`-module. + It is the direct sum of a free module of rank `1` generated by `L`, and + a free rank one `R` module generated by `C` satisfying `TC = 0`. `C` + is central (the `\lambda`-bracket of `C` with any other vector + vanishes). The remaining `\lambda`-bracket is given by + + .. MATH:: + + [L_\lambda L] = T L + 2 \lambda L + \frac{\lambda^3}{12} C. + +2. The **affine** or current Lie conformal algebra `L(\mathfrak{g})` + associated to a finite dimensional Lie algebra `\mathfrak{g}` with + non-degenerate, invariant `R`-bilinear form `(,)` is given as a central + extension of the free + `R[T]` module generated by `\mathfrak{g}` by a central element `K`. The + `\lambda`-bracket of generators is given by + + .. MATH:: + + [a_\lambda b] = [a,b] + \lambda (a,b) K, \qquad a,b \in \mathfrak{g} + +3. The **Weyl** Lie conformal algebra, or `\beta-\gamma` system is + given as the central extension of a free `R[T]` module with two + generators `\beta` and `\gamma`, by a central element `K`. + The only non-trivial brackets among generators are + + .. MATH:: + + [\beta_\lambda \gamma] = - [\gamma_\lambda \beta] = K + +4. The **Neveu-Schwarz** super Lie conformal algebra is a super Lie + conformal algebra which is an extension of the Virasoro Lie conformal + algebra. It consists of a Virasoro generator `L` as in example 1 above + and an *odd* generator `G`. The remaining brackets are given by: + + .. MATH:: + + [L_\lambda G] = \left( T + \frac{3}{2} \lambda \right) G \qquad + [G_\lambda G] = 2 L + \frac{\lambda^2}{3} C + +.. SEEALSO:: + + :mod:`sage.algebras.lie_conformal_algebras.examples` + +The base class for all Lie conformal algebras is +:class:`LieConformalAlgebra`. +All subclasses are called through its method ``__classcall_private__``. +This class provides no functionality besides calling the appropriate +constructor. + +EXAMPLES: + +- We construct the Virasoro Lie conformal algebra, its universal + enveloping vertex algebra and lift some elements:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.inject_variables() + Defining L, C + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + +- We construct the Current algebra for `\mathfrak{sl}_2`:: + + sage: R = AffineLieConformalAlgebra(QQ, 'A1', names = ('e', 'h', 'f')) + sage: R.gens() + (e, h, f, K) + sage: R.inject_variables() + Defining e, h, f, K + sage: e.bracket(f.T()) + {0: Th, 1: h, 2: 2*K} + sage: e.T(3) + 6*T^(3)e + +- We construct the `\beta-\gamma` system by directly giving the + `\lambda`-brackets of the generators:: + + sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} + sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) + sage: V.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: V.inject_variables() + Defining a, b, K + sage: a.bracket(b) + {0: -K} + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.sets.family import Family +from sage.categories.commutative_rings import CommutativeRings + +class LieConformalAlgebra(UniqueRepresentation): + r""" + Lie Conformal Algebras base class and factory. + + INPUT: + + - ``R`` -- a commutative ring (default: ``None``); the base + ring of this Lie conformal algebra. Behaviour is undefined + if it is not a field of characteristic zero. + + - ``arg0`` -- a dictionary (default: ``None``); + a dictionary containing the `\lambda` brackets of the + generators of this Lie conformal algebra. The keys of this + dictionary are pairs of either names or indices of the + generators and the values are themselves dictionaries. For a + pair of generators ``'a'`` and ``'b'``, the value of + ``arg0[('a','b')]`` is a dictionary whose keys are positive + integer numbers and the corresponding value for the + key ``j`` is a dictionary itself representing the j-th product + `a_{(j)}b`. Thus, for a positive integer number `j`, the + value of ``arg0[('a','b')][j]`` is a dictionary whose entries + are pairs ``('c',n)`` where ``'c'`` is the name of a generator + and ``n`` is a positive number. The value for this key is the + coefficient of `\frac{T^{n}}{n!} c` in `a_{(j)}b`. For + example the ``arg0`` for the *Virasoro* Lie conformal algebra + is:: + + {('L','L'):{0:{('L',1):1}, 1:{('L',0):2}, 3:{('C',0):1/2}}} + + + Do not include central elements as keys in this dictionary. Also, + if the key ``('a','b')`` is present, there is no need to include + ``('b','a')`` as it is defined by skew-symmetry. Any missing + pair (besides the ones defined by skew-symmetry) is assumed + to have vanishing `\lambda`-bracket. + + - ``names`` -- tuple of ``str`` (default: ``None``); the list of + names for generators of this Lie conformal algebra. Do not + include central elements in this list. + + - ``central_elements`` -- tuple of ``str`` (default: ``None``); + A list of names for central elements of this Lie conformal + algebra. + + - ``index_set`` -- enumerated set (default: ``None``); an + indexing set for the generators of this Lie conformal algebra. + Do not include central elements in this list. + + - ``weights`` -- tuple of non-negative rational numbers + (default: ``None``); a list of degrees for this Lie + conformal algebra. + The returned Lie conformal algebra is H-Graded. This tuple + needs to have the same cardinality as ``index_set`` or + ``names``. Central elements are assumed to have weight `0`. + + - ``parity`` -- tuple of `0` or `1` (default: tuple of `0`); + if this is a super Lie conformal algebra, this tuple + specifies the parity of each of the non-central generators of + this Lie conformal algebra. Central elements are assumed to + be even. Notice that if this tuple is present, the category + of this Lie conformal algebra is set to be a subcategory of + ``LieConformalAlgebras(R).Super()``, even if all generators + are even. + + - ``category`` The category that this Lie conformal algebra + belongs to. + + In addition we accept the following keywords: + + - ``graded`` -- a boolean (default: ``False``); + if ``True``, the returned algebra is H-Graded. + If ``weights`` is not specified, all non-central generators + are assigned degree `1`. This keyword is ignored if + ``weights`` is specified + + - ``super`` -- a boolean (default: ``False``); + if ``True``, the returned algebra is a super + Lie conformal algebra even if all generators are even. + If ``parity`` is not specified, all generators are + assigned even parity. This keyword is ignored if + ``parity`` is specified. + + .. Note:: + + Any remaining keyword is currently passed to + :class:`CombinatorialFreeModule`. + + EXAMPLES: + + We construct the `\beta-\gamma` system or *Weyl* Lie conformal + algebra:: + + sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} + sage: V = LieConformalAlgebra(QQbar, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) + sage: V.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Algebraic Field + sage: V.inject_variables() + Defining a, b, K + sage: a.bracket(b) + {0: -K} + + We construct the current algebra for `\mathfrak{sl}_2`:: + + sage: sl2dict = {('e','f'):{0:{('h',0):1}, 1:{('K',0):1}}, ('e','h'):{0:{('e',0):-2}}, ('f','h'):{0:{('f',0):2}}, ('h', 'h'):{1:{('K', 0):2}}} + sage: V = LieConformalAlgebra(QQ, sl2dict, names=('e', 'h', 'f'), central_elements=('K',), graded=True) + sage: V.inject_variables() + Defining e, h, f, K + sage: e.bracket(f) + {0: h, 1: K} + sage: h.bracket(e) + {0: 2*e} + sage: e.bracket(f.T()) + {0: Th, 1: h, 2: 2*K} + sage: V.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: e.degree() + 1 + + .. TODO:: + + This class checks that the provided dictionary is consistent + with skew-symmetry. It does not check that it is consistent + with the Jacobi identity. + + .. SEEALSO:: + + :mod:`sage.algebras.lie_conformal_algebras.graded_lie_conformal_algebra` + """ + @staticmethod + def __classcall_private__(cls, R=None, arg0=None, index_set=None, + central_elements=None, category=None, prefix=None, + names=None, latex_names=None, parity=None, weights=None, **kwds): + + if not R in CommutativeRings(): + raise ValueError("First argument must be a commutative ring" + + " got {}".format(R)) + + #This is the only exposed class so we clean keywords here + known_keywords = ['category', 'prefix', 'bracket', 'latex_bracket', + 'string_quotes', 'sorting_key', 'graded', 'super'] + for key in kwds: + if key not in known_keywords: + raise ValueError("LieConformalAlgebra(): got an unexpected " + + "keyword argument '%s'"%key) + + if isinstance(arg0,dict) and arg0: + graded=kwds.pop("graded", False) + if weights is not None or graded: + from .graded_lie_conformal_algebra import \ + GradedLieConformalAlgebra + return GradedLieConformalAlgebra(R, Family(arg0), + index_set=index_set, central_elements=central_elements, + category=category, prefix=prefix, names=names, + latex_names=latex_names, parity=parity, weights=weights, + **kwds) + else: + from .lie_conformal_algebra_with_structure_coefs import \ + LieConformalAlgebraWithStructureCoefficients + return LieConformalAlgebraWithStructureCoefficients(R, + Family(arg0), index_set=index_set, + central_elements=central_elements, category=category, + prefix=prefix, names=names, latex_names=latex_names, + parity=parity, **kwds) + return NotImplementedError("Not implemented") + diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py new file mode 100644 index 00000000000..de270aa1a74 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -0,0 +1,324 @@ +""" +Lie Conformal Algebra Element + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.functions.other import factorial +from sage.misc.misc_c import prod +from sage.misc.misc import repr_lincomb +from sage.misc.latex import latex +from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement + +class LCAWithGeneratorsElement(IndexedFreeModuleElement): + """ + The element class of a Lie conformal algebra with a + preferred set of generators. + """ + def T(self,n=1): + r""" + The n-th derivative of this element. + + INPUT: + + - ``n`` -- a non-negative integer (default:``1``); how many + times to apply `T` to this element. + + We use the *divided powers* notation + `T^{(j)} = \frac{T^j}{j!}`. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.inject_variables() + Defining L, C + sage: L.T() + TL + sage: L.T(3) + 6*T^(3)L + sage: C.T() + 0 + + sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); R.inject_variables() + Defining L, G, C + sage: (L + 2*G.T() + 4*C).T(2) + 2*T^(2)L + 12*T^(3)G + + TESTS:: + + sage: R = VirasoroLieConformalAlgebra(QQ); R.zero().T() + 0 + sage: (R.zero() + R.0).T() + TL + sage: R.0.T(0) + L + """ + from sage.rings.all import NN + if n not in NN: + raise ValueError("n must be a nonnegative Integer") + if n == 0 or self.is_zero(): + return self + #it's faster to sum than to use recursion + if self.is_monomial(): + p = self.parent() + a,m = self.index() + coef = self._monomial_coefficients[(a,m)] + if (a,m+n) in p._indices: + return coef*prod(j for j in range(m+1,m+n+1))\ + *p.monomial((a,m+n)) + else: + return p.zero() + return sum(mon.T(n) for mon in self.terms()) + + def is_monomial(self): + """ + Whether this element is a monomial. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: (L + L.T()).is_monomial() + False + sage: L.T().is_monomial() + True + + TESTS:: + + sage: R = AffineLieConformalAlgebra(AA,'A1',names=('e','h','f')); R.inject_variables() + Defining e, h, f, K + sage: R.zero().is_monomial() + True + sage: (e.T(2) + R.zero()).is_monomial() + True + """ + return len(self._monomial_coefficients) == 1 or self.is_zero() + + +class LCAStructureCoefficientsElement(LCAWithGeneratorsElement): + """ + An element of a Lie conformal algebra given by structure + coefficients. + """ + + def is_even_odd(self): + """ + Return ``0`` if this element is even or ``1`` if it is odd. + + EXAMPLES:: + + sage: R = NeveuSchwarzLieConformalAlgebra(QQ); R.inject_variables() + Defining L, G, C + sage: L.is_even_odd() + 0 + sage: G.is_odd() + True + """ + if self.is_zero(): + return 0 + p = self.parent() + coefs = self.monomial_coefficients() + paritylist = [p._parity[p.monomial((k,0))] for k,v in coefs] + if paritylist[1:] == paritylist[:-1]: + return paritylist[0] + raise ValueError("{} is not homogeneous".format(self)) + + def _bracket_(self, right): + """ + The lambda bracket of these two elements. + + The result is a dictionary with non-negative integer keys. + The value corresponding to the entry `j` is ``self_{(j)}right``. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L.T().bracket(L) + {1: -TL, 2: -4*L, 4: -2*C} + + sage: R = AffineLieConformalAlgebra(QQbar, 'A1', names=('e','h','f')); R + The affine Lie conformal algebra of type ['A', 1] over Algebraic Field + sage: R.inject_variables() + Defining e, h, f, K + sage: e.bracket(f) + {0: h, 1: K} + sage: h.bracket(h.T()) + {2: 4*K} + """ + p = self.parent() + if self.is_monomial() and right.is_monomial(): + if self.is_zero() or right.is_zero(): + return {} + s_coeff = p._s_coeff + a,k = self.index() + coefa = self.monomial_coefficients()[(a,k)] + b,m = right.index() + coefb = right.monomial_coefficients()[(b,m)] + try: + mbr = dict(s_coeff[(a,b)]) + except KeyError: + return {} + pole = max(mbr.keys()) + ret = {l: coefa*coefb*(-1)**k/factorial(k)*sum(factorial(l)\ + /factorial(m+k+j-l)/factorial(l-k-j)/factorial(j)*\ + mbr[j].T(m+k+j-l) for j in mbr if j >= l-m-k and\ + j <= l-k) for l in range(m+k+pole+1)} + return {k:v for k,v in ret.items() if v} + + diclist = [i._bracket_(j) for i in self.terms() for + j in right.terms()] + ret = {} + pz = p.zero() + for d in diclist: + for k in d.keys(): + ret[k] = ret.get(k,pz) + d[k] + return {k:v for k,v in ret.items() if v} + + def _repr_(self): + r""" + A visual representation of this element. + + For a free generator `L`, the element `\frac{T^{j}}{j!}L` is + denoted by ``T^(j)L``. + + EXAMPLES:: + + sage: V = VirasoroLieConformalAlgebra(QQ); V.inject_variables() + Defining L, C + sage: v = L.T(5).nproduct(L,6); v + -1440*L + sage: L.T(2) + L + C + 2*T^(2)L + L + C + sage: L.T(4) + 24*T^(4)L + + sage: R = AffineLieConformalAlgebra(QQ, 'B3') + sage: R.2.T()+3*R.3 + TB[alpha[1]] + 3*B[alpha[2] + alpha[3]] + """ + if self.is_zero(): + return "0"; + p = self.parent() + if p._names: + terms = [("T^({0}){1}".format(k[1], + p._names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ + else("T{}".format(p._names[p._index_to_pos[k[0]]]),v) \ + if k[1] == 1 \ + else ("{}".format(p._names[p._index_to_pos[k[0]]]),v)\ + for k,v in self.monomial_coefficients().items()] + else: + terms = [("T^({0}){1}".format(k[1], p._repr_generator(k[0])),v)\ + if k[1] > 1 else("T{}".format(p._repr_generator(k[0])),v)\ + if k[1] == 1 else ("{}".format(p._repr_generator(k[0])), + v) for k,v in self.monomial_coefficients().items()] + + return repr_lincomb(terms, strip_one=True) + + def _latex_(self): + r""" + A visual representation of this element. + + For a free generator `L`, the element `\frac{T^{j}}{j!}L` is + denoted by ``T^(j)L``. + + EXAMPLES:: + + sage: V = VirasoroLieConformalAlgebra(QQ); V.inject_variables() + Defining L, C + sage: latex(L.T(2)) + 2T^{(2)}L + + sage: R = AffineLieConformalAlgebra(QQbar, 'A1', names=('e','h','f')); R.inject_variables() + Defining e, h, f, K + sage: latex(e.bracket(f)) + \left\{0 : h, 1 : K\right\} + sage: latex(e.T(3)) + 6T^{(3)}e + + sage: R = AffineLieConformalAlgebra(QQbar, 'A1') + sage: latex(R.0.bracket(R.2)) + \left\{0 : \alpha^\vee_{1}, 1 : \text{\texttt{K}}\right\} + + + sage: R = AffineLieConformalAlgebra(QQ, 'A1'); latex(R.0.T(3)) + 6T^{(3)}\alpha_{1} + + """ + if self.is_zero(): + return "0"; + p = self.parent() + try: + names = p.latex_variable_names() + except ValueError: + names = None + if names: + terms = [("T^{{({0})}}{1}".format(k[1], + names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ + else("T{}".format(names[p._index_to_pos[k[0]]]),v)\ + if k[1] == 1\ + else ("{}".format(names[p._index_to_pos[k[0]]]),v)\ + for k,v in self.monomial_coefficients().items()] + else: + terms = [("T^{{({0})}}{1}".format(k[1], latex(k[0])),v) if k[1] > 1 \ + else("T{}".format(latex(k[0])),v) if k[1] == 1 \ + else ("{}".format(latex(k[0])),v)\ + for k,v in self.monomial_coefficients().items()] + + return repr_lincomb(terms, is_latex=True, strip_one = True) + +class GradedLCAElement(LCAStructureCoefficientsElement): + """ + Base class for H-graded Lie conformal algebras with structure + coefficients. + """ + def degree(self): + """ + The degree of this element. + + EXAMPLES:: + + sage: V = VirasoroLieConformalAlgebra(QQ) + sage: V.inject_variables() + Defining L, C + sage: C.degree() + 0 + sage: L.T(4).degree() + 6 + + sage: N = NeveuSchwarzLieConformalAlgebra(AA) + sage: N.inject_variables() + Defining L, G, C + sage: G.T().degree() + 5/2 + sage: L.degree() + 2 + sage: N.zero().degree() + +Infinity + """ + if self.is_zero(): + from sage.rings.infinity import Infinity + return Infinity + p = self.parent() + ls = [] + for gen,n in self.monomial_coefficients(): + ret = n + if gen not in p._central_elements: + ret+= p._weights[p._index_to_pos[gen]] + ls.append(ret) + if ls[1:] == ls[:-1]: + return ls[0] + raise ValueError("{} is not homogeneous!".format(self)) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py new file mode 100644 index 00000000000..340813221b2 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py @@ -0,0 +1,63 @@ +""" +Lie Conformal Algebras With Basis + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.lie_conformal_algebras import LieConformalAlgebras +from sage.combinat.free_module import CombinatorialFreeModule + +class LieConformalAlgebraWithBasis(CombinatorialFreeModule): + def __init__(self,R, basis_keys=None, element_class=None, category=None, + prefix=None, **kwds): + """ + Abstract base class for a Lie conformal algebra with a + preferred basis. + + This class provides no functionality, it simply passes the + arguments to :class:`CombinatorialFreeModule`. + + EXAMPLES:: + + sage: R = VirasoroLieConformalAlgebra(QQbar);R + The Virasoro Lie conformal algebra over Algebraic Field + + TESTS:: + + sage: R = VirasoroLieConformalAlgebra(QQ) + sage: R.0 + L + sage: R._repr_generator(R.0) + 'L' + sage: R = AffineLieConformalAlgebra(QQ, 'A1') + sage: R.0 + B[alpha[1]] + sage: R._repr_generator(R.0) + 'B[alpha[1]]' + sage: R = AffineLieConformalAlgebra(QQ, 'A1', names = ('e', 'h','f')) + sage: R.0 + e + sage: R._repr_generator(R.0) + 'e' + """ + if prefix is None: + prefix = '' + kwds['bracket'] = '' + kwds['string_quotes'] = False + + category = LieConformalAlgebras(R).WithBasis().or_subcategory(category) + super(LieConformalAlgebraWithBasis,self).__init__(R, + basis_keys=basis_keys, element_class=element_class, + category=category, prefix=prefix, names=None, **kwds) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py new file mode 100644 index 00000000000..5d2133c6809 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py @@ -0,0 +1,97 @@ +""" +Lie Conformal Algebras With Generators. + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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 .lie_conformal_algebra_with_basis import LieConformalAlgebraWithBasis +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.categories.cartesian_product import cartesian_product +from sage.rings.integer import Integer +from sage.sets.family import Family +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets + +class LieConformalAlgebraWithGenerators(LieConformalAlgebraWithBasis): + def __init__(self,R, index_set=None, central_elements=None, category=None, + element_class=None, prefix=None, **kwds): + """ + Base class for a Lie conformal algebra with distinguished + generators. + + This class provides minimal functionality, it sets up the + family of Lie conformal algebra generators. + + .. NOTE:: + + We now only accept direct sums of free modules plus + some central generators `C_i` such that `TC_i = 0`. + """ + self._generators = Family(index_set) + E = cartesian_product([index_set, NonNegativeIntegers()]) + if central_elements is not None: + self._generators = DisjointUnionEnumeratedSets([index_set, + Family(central_elements)]) + E = DisjointUnionEnumeratedSets((cartesian_product([ + Family(central_elements), {Integer(0)}]),E)) + + super(LieConformalAlgebraWithGenerators,self).__init__(R, basis_keys=E, + element_class=element_class, category=category, prefix=prefix, + **kwds) + + if central_elements is not None: + self._central_elements = Family(central_elements) + else: + self._central_elements = tuple() + + def lie_conformal_algebra_generators(self): + """ + The generators of this Lie conformal algebra. + + OUTPUT: a (possibly infinite) family of generators (as an + `R[T]`-module) of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.lie_conformal_algebra_generators() + (L, C) + sage: V = AffineLieConformalAlgebra(QQ,'A1') + sage: V.lie_conformal_algebra_generators() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + """ + F = Family(self._generators, + lambda i: self.monomial((i,Integer(0))), + name = "generator map") + from sage.categories.sets_cat import Sets + if F in Sets().Finite(): + return tuple(F) + return F + + def central_elements(self): + """ + The central generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.central_elements() + (C,) + sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V.central_elements() + (B['K'],) + """ + return Family(self._central_elements, + lambda i: self.monomial((i,Integer(0))), + name = "central_element map") diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py new file mode 100644 index 00000000000..c1f74e9b09c --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -0,0 +1,309 @@ +""" +Lie Conformal Algebras With Structure Coefficients + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. + +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.functions.other import binomial +from sage.structure.indexed_generators import (IndexedGenerators, + standardize_names_index_set) +from sage.sets.family import Family +from .lie_conformal_algebra_element import LCAStructureCoefficientsElement +from sage.categories.lie_conformal_algebras import LieConformalAlgebras + +from .finitely_generated_lie_conformal_algebra import \ + FinitelyGeneratedLieConformalAlgebra +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets + +class LieConformalAlgebraWithStructureCoefficients( + FinitelyGeneratedLieConformalAlgebra): + @staticmethod + def _standardize_s_coeff(s_coeff, index_set, ce, parity=None): + """ + Convert an input dictionary to structure constants of this + Lie conformal algebra. + + INPUT: + + - ``s_coeff`` -- a dictionary as in + :class:`LieConformalAlgebraWithStructureCoefficients`. + - ``index_set` -- A finite enumerated set indexing the + generators (not counting the central elements). + - ``ce`` -- a tuple of ``str``; a list of names for the central + generators of this Lie conformal algebra + - ``parity`` -- a tuple of `0` or `1` (Default: tuple of `0`); + this tuple specifies the parity of each non-central generator. + + OUTPUT: + + A finite Family representing ``s_coeff`` in the input. + It contains superfluous information that can be obtained by + skew-symmetry but that improves speed in computing OPE for + vertex algebras. + """ + if parity is None: + parity = (0,)*index_set.cardinality() + index_to_parity = {i:p for (i,p) in zip(index_set,parity)} + sc = {} + #mypair has a pair of generators + for mypair in s_coeff.keys(): + #e.g. v = { 0: { (L,2):3, (G,3):1}, 1:{(L,1),2} } + v = s_coeff[mypair] + key = tuple(mypair) + vals={} + for l in v.keys(): + lth_product = {k:y for k,y in v[l].items() if y} + if lth_product: + vals[l]=lth_product + + myvals = tuple([(k,tuple(v.items())) for k,v in vals.items() if v]) + + if key in sc.keys() and sorted(sc[key]) != sorted(myvals): + raise ValueError("two distinct values given for one "\ + "and the same bracket, skew-symmetry"\ + "is not satisfied?") + if myvals: + sc[key] = myvals + + #We now add the skew-symmetric part to optimize + #brackets computations later + key=(mypair[1],mypair[0]) + if index_to_parity[mypair[0]]*index_to_parity[mypair[1]]: + parsgn = -1 + else: + parsgn = 1 + maxpole = max(v.keys()) + vals={} + for k in range(maxpole+1): + kth_product = {} + for j in range(maxpole+1-k): + if k+j in v.keys(): + for i in v[k+j]: + if (i[0] not in ce) or ( + i[0] in ce and i[1] + j == 0): + kth_product[(i[0],i[1]+j)] = \ + kth_product.get((i[0], i[1]+j), 0) + kth_product[(i[0],i[1]+j)] += parsgn*\ + v[k+j][i]*(-1)**(k+j+1)*binomial(i[1]+j,j) + kth_product = {k:v for k,v in kth_product.items() if v} + if kth_product: + vals[k]=kth_product + + myvals = tuple([(k,tuple(v.items())) for k,v in vals.items() if v]) + + if key in sc.keys() and sorted(sc[key]) != sorted(myvals): + raise ValueError("two distinct values given for one "\ + "and the same bracket. "\ + "Skew-symmetry is not satisfied?") + if myvals: + sc[key] = myvals + return Family(sc) + + def __init__(self, R, s_coeff, index_set=None, central_elements=None, + category=None, element_class=None, prefix=None, names=None, + latex_names=None, parity=None, **kwds): + r""" + A Lie conformal algebra with a set of specified structure + coefficients. + + INPUT: + + - ``R`` -- a ring (Default: ``None``); The base ring of this Lie + conformal algebra. Behaviour is undefined if it is not a field + of characteristic zero. + + - ``s_coeff`` -- Dictionary (Default: ``None``); + a dictionary containing the `\lambda` brackets of the + generators of this Lie conformal algebra. The family encodes a + dictionary whose keys + are pairs of either names or indices of the generators + and the values are themselves dictionaries. For a pair of + generators `a` and `b`, the value of ``s_coeff[('a','b')]`` is + a dictionary whose keys are positive integer numbers and the + corresponding value for the key `j` is a dictionary itself + representing the j-th product `a_{(j)}b`. + Thus, for a positive integer number `j`, the value of + ``s_coeff[('a','b')][j]`` is a dictionary whose entries are + pairs ``('c',n)`` where ``'c'`` is the name of a generator + and `n` is a positive number. The value for this key is the + coefficient of `\frac{T^{n}}{n!} c` in `a_{(j)}b`. For example + the ``s_coeff`` for the *Virasoro* Lie conformal algebra is:: + + {('L','L'):{0:{('L',1):1}, 1:{('L',0):2}, 3:{('C',0):1/2}}} + + + Do not include central elements in this dictionary. Also, if + the key ``('a','b')`` is present, there is no need to include + ``('b','a')`` as it is defined by skew-symmetry. + Any missing pair (besides the ones + defined by skew-symmetry) is assumed to have vanishing + `\lambda`-bracket. + + - ``names`` -- tuple of ``str`` (Default: ``None``); The list of + names for generators of this Lie conformal algebra. Do not + include central elements in this list. + + - ``central_elements`` -- tuple of ``str`` (Default: ``None``); + A list of names for central + elements of this Lie conformal algebra. + + - ``index_set`` -- enumerated set (Default: ``None``); + an indexing set for the generators of this Lie + conformal algebra. Do not include central elements in this + list. + + - ``parity`` -- tuple of `0` or `1` (Default: tuple of `0`); + a tuple specifying the parity of each non-central generator. + + EXAMPLES: + + - We construct the `\beta-\gamma` system by directly giving the + `\lambda`-brackets of the generators:: + + sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} + sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) + sage: V.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: V.inject_variables() + Defining a, b, K + sage: a.bracket(b) + {0: -K} + + - We construct the centerless Virasoro Lie conformal algebra:: + + sage: virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}}} + sage: R = LieConformalAlgebra(QQbar, virdict, names='L') + sage: R.inject_variables() + Defining L + sage: L.bracket(L) + {0: TL, 1: 2*L} + + - The construction checks that skew-symmetry is violated:: + + sage: wrongdict = {('L','L'):{0:{('L',1):2}, 1:{('L',0): 2}}} + sage: LieConformalAlgebra(QQbar, wrongdict, names='L') + Traceback (most recent call last): + ... + ValueError: two distinct values given for one and the same bracket. Skew-symmetry is not satisfied? + """ + names, index_set = standardize_names_index_set(names,index_set) + + if central_elements is None: + central_elements= tuple() + + if names is not None and names != tuple(index_set): + names2 = names + tuple(central_elements) + index_set2 = DisjointUnionEnumeratedSets((index_set, + Family(tuple(central_elements)))) + d = {x:index_set2[i] for i,x in enumerate(names2)} + try: + #If we are given a dictionary with names as keys, + #convert to index_set as keys + s_coeff = {(d[k[0]],d[k[1]]):{a:{(d[x[1]],x[2]): + s_coeff[k][a][x] for x in + s_coeff[k][a]} for a in s_coeff[k]} for k in s_coeff.keys()} + + except KeyError: + # We assume the dictionary was given with keys in the + # index_set + pass + + issuper=kwds.pop('super', False) + if parity is None: + parity = (0,)*index_set.cardinality() + else: + issuper = True + + try: + assert len(parity) == index_set.cardinality() + except AssertionError: + raise ValueError("parity should have the same length as the "\ + "number of generators, got {}".format(parity)) + + s_coeff = LieConformalAlgebraWithStructureCoefficients\ + ._standardize_s_coeff(s_coeff, index_set, central_elements, + parity) + + if names is not None and central_elements is not None: + names += tuple(central_elements) + + self._index_to_pos = {k: i for i,k in enumerate(index_set)} + #Add central parameters to index_to_pos so that we can + #represent names + if central_elements is not None: + for i,ce in enumerate(central_elements): + self._index_to_pos[ce] = len(index_set)+i + + category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated()\ + .or_subcategory(category) + if issuper: + category = category.Super() + + if element_class is None: + element_class=LCAStructureCoefficientsElement + + FinitelyGeneratedLieConformalAlgebra.__init__( + self, R, index_set=index_set, central_elements=central_elements, + category=category, element_class=element_class, + prefix=prefix, names=names, latex_names=latex_names, **kwds) + + s_coeff=dict(s_coeff) + self._s_coeff = Family({k: tuple((j, sum(c*self.monomial(i) + for i,c in v )) for j,v in s_coeff[k]) for k in s_coeff}) + self._parity = dict(zip(self.gens(),parity+(0,)*len(central_elements))) + + def structure_coefficients(self): + """ + The structure coefficients of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(AA) + sage: Vir.structure_coefficients() + Finite family {('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} + + sage: NeveuSchwarzLieConformalAlgebra(QQ).structure_coefficients() + Finite family {('G', 'G'): ((0, 2*L), (2, 2/3*C)), ('G', 'L'): ((0, 1/2*TG), (1, 3/2*G)), ('L', 'G'): ((0, TG), (1, 3/2*G)), ('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} + """ + return self._s_coeff + + def _repr_generator(self, x): + """ + String representation of the generator ``x``. + + INPUT: + + - ``x`` -- an index parametrizing a generator or a generator of + this Lie conformal algebra + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQbar) + sage: Vir._repr_generator(Vir.0) + 'L' + sage: R = AffineLieConformalAlgebra(QQ, 'A1') + sage: R._repr_generator(R.0) + 'B[alpha[1]]' + sage: R = AffineLieConformalAlgebra(QQ, 'A1', names=('e','h','f')) + sage: R._repr_generator(R.0) + 'e' + """ + if x in self: + return repr(x) + return IndexedGenerators._repr_generator(self,x) diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index a60c45eba95..9023ce3948a 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -138,3 +138,6 @@ # polyhedra lazy_import('sage.categories.polyhedra', 'PolyhedralSets') + +# vertex algebras +lazy_import('sage.categories.lie_conformal_algebras', 'LieConformalAlgebras') diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 524fb42c386..6a609047faa 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1680,7 +1680,9 @@ class ``Sets.Finite``), or in a separate file (typically in a class "Facade", "Finite", "Infinite","Enumerated", "Complete", "Nilpotent", - "FiniteDimensional", "Connected", "WithBasis", + "FiniteDimensional", "Connected", + "FinitelyGeneratedAsLieConformalAlgebra", + "WithBasis", "Irreducible", "Supercommutative", "Supercocommutative", "Commutative", "Cocommutative", "Associative", "Inverse", "Unital", "Division", "NoZeroDivisors", "Cellular", @@ -2284,6 +2286,8 @@ def _repr_object_names_static(category, axioms): elif axiom == "FinitelyGeneratedAsMagma" and \ not base_category.is_subcategory(AdditiveMagmas()): result = "finitely generated " + result + elif axiom == "FinitelyGeneratedAsLieConformalAlgebra": + result = "finitely generated " + result else: result = uncamelcase(axiom) + " " + result return result diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py new file mode 100644 index 00000000000..c125cd503cc --- /dev/null +++ b/src/sage/categories/lie_conformal_algebras.py @@ -0,0 +1,940 @@ +r""" +Lie Conformal Algebras + +Let `R` be a commutative ring, a *super Lie conformal algebra* +[Kac1997]_ over `R` +(also known as a *vertex Lie algebra*) is an `R[T]` super module `L` +together with a `\mathbb{Z}/2\mathbb{Z}`-graded `R`-bilinear +operation (called the `\lambda`-bracket) +`L\otimes L \rightarrow L[\lambda]` +(polynomials in `\lambda` with +coefficients in `L`), `a \otimes b \mapsto [a_\lambda b]` satisfying + +1. Sesquilinearity: + + .. MATH:: + + [Ta_\lambda b] = - \lambda [a_\lambda b], \qquad [a_\lambda Tb] = + (\lambda+ T) [a_\lambda b]. + +2. Skew-Symmetry: + + .. MATH:: + + [a_\lambda b] = - (-1)^{p(a)p(b)} [b_{-\lambda - T} a], + + where `p(a)` is `0` if `a` is *even* and `1` if `a` is *odd*. The + bracket in the RHS is computed as follows. First we evaluate + `[b_\mu a]` with the formal + parameter `\mu` to the *left*, then + replace each appearance of the formal variable `\mu` by `-\lambda - T`. + Finally apply `T` to the coefficients in `L`. + +3. Jacobi identity: + + .. MATH:: + + [a_\lambda [b_\mu c]] = [ [a_{\lambda + \mu} b]_\mu c] + + (-1)^{p(a)p(b)} [b_\mu [a_\lambda c ]], + + which is understood as an equality in `L[\lambda,\mu]`. + + `T` is usually called the *translation operation* or the *derivative*. + For an element `a \in L` we will say that `Ta` is the *derivative of* + `a`. We define the *n-th products* `a_{(n)} b` for `a,b \in L` by + + .. MATH:: + + [a_\lambda b] = \sum_{n \geq 0} \frac{\lambda^n}{n!} a_{(n)} b. + + A Lie conformal algebra is called *H-Graded* [DSK2006]_ if there exists + a decomposition `L = \oplus L_n` such that the + `\lambda`-bracket becomes graded of degree `-1`, that is: + + .. MATH:: + + a_{(n)} b \in L_{p + q -n -1} \qquad + a \in L_p, \: b \in L_q, \: n \geq 0. + + In particular this implies that the action of `T` increases + degree by `1`. + +.. NOTE:: + + In the literature arbitrary gradings are allowed. In this + implementation we only support non-negative rational gradings. + + +EXAMPLES: + +1. The **Virasoro** Lie conformal algebra `Vir` over a ring `R` + where `12` is invertible has two generators `L, C` as an `R[T]`-module. + It is the direct sum of a free module of rank `1` generated by `L`, and + a free rank one `R` module generated by `C` satisfying `TC = 0`. `C` + is central (the `\lambda`-bracket of `C` with any other vector + vanishes). The remaining `\lambda`-bracket is given by + + .. MATH:: + + [L_\lambda L] = T L + 2 \lambda L + \frac{\lambda^3}{12} C. + +2. The **affine** or current Lie conformal algebra `L(\mathfrak{g})` + associated to a finite dimensional Lie algebra `\mathfrak{g}` with + non-degenerate, invariant `R`-bilinear form `(,)` is given as a central + extension of the free + `R[T]` module generated by `\mathfrak{g}` by a central element `K`. The + `\lambda`-bracket of generators is given by + + .. MATH:: + + [a_\lambda b] = [a,b] + \lambda (a,b) K, \qquad a,b \in \mathfrak{g} + +3. The **Weyl** Lie conformal algebra, or `\beta-\gamma` system is + given as the central extension of a free `R[T]` module with two + generators `\beta` and `\gamma`, by a central element `K`. + The only non-trivial brackets among generators are + + .. MATH:: + + [\beta_\lambda \gamma] = - [\gamma_\lambda \beta] = K + +4. The **Neveu-Schwarz** super Lie conformal algebra is a super Lie + conformal algebra which is an extension of the Virasoro Lie conformal + algebra. It consists of a Virasoro generator `L` as in example 1 above + and an *odd* generator `G`. The remaining brackets are given by: + + .. MATH:: + + [L_\lambda G] = \left( T + \frac{3}{2} \lambda \right) G \qquad + [G_\lambda G] = 2 L + \frac{\lambda^2}{3} C + +.. SEEALSO:: + + - :mod:`sage.algebras.lie_conformal_algebras.lie_conformal_algebra` + - :mod:`sage.algebras.lie_conformal_algebras.examples` + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.modules import Modules +from .category_types import Category_over_base_ring +from sage.categories.category_with_axiom import \ + CategoryWithAxiom_over_base_ring +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.structure.element import coerce_binop +from sage.categories.graded_modules import GradedModulesCategory +from sage.categories.super_modules import SuperModulesCategory +from sage.categories.commutative_rings import CommutativeRings +_CommutativeRings = CommutativeRings() + +class LieConformalAlgebras(Category_over_base_ring): + r""" + The category of Lie conformal algebras. + + This is the base category for all Lie conformal algebras. + Subcategories with axioms are ``FinitelyGenerated`` and + ``WithBasis``. A *finitely generated* Lie conformal algebra is a + Lie conformal algebra over `R` which is finitely generated as an + `R[T]`-module. A Lie conformal algebra *with basis* is one with a + preferred basis as an `R`-module. + + EXAMPLES: + + The base category:: + + sage: C = LieConformalAlgebras(QQ); C + Category of Lie conformal algebras over Rational Field + sage: C.is_subcategory(VectorSpaces(QQ)) + True + + Some subcategories:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().WithBasis() + Category of finitely generated Lie conformal algebras with basis over Algebraic Field + + In addition we support functorial constructions ``Graded`` and + ``Super``. These functors commute:: + + sage: LieConformalAlgebras(AA).Graded().Super() + Category of super H-graded Lie conformal algebras over Algebraic Real Field + sage: LieConformalAlgebras(AA).Graded().Super() is LieConformalAlgebras(AA).Super().Graded() + True + + That is, we only consider gradings on super Lie conformal algebras + that are compatible with the `\mathbb{Z}/2\mathbb{Z}` grading. + All four subcategories commute in this sense:: + + sage: C = LieConformalAlgebras(AA).Graded().FinitelyGenerated().WithBasis().Super() + sage: D = LieConformalAlgebras(AA).Super().WithBasis().Graded().FinitelyGenerated() + sage: C is D + True + sage: C + Category of finitely generated super H-graded Lie conformal algebras with basis over Algebraic Real Field + + The base ring needs to be a commutative ring:: + + sage: LieConformalAlgebras(QuaternionAlgebra(2)) + Traceback (most recent call last): + ValueError: base must be a commutative ring got Quaternion Algebra (-1, -1) with base ring Rational Field + """ + + @staticmethod + def __classcall_private__(cls,R,check=True): + r""" + INPUT: + + - `R` -- a commutative ring. + - ``check`` -- a boolean (default: ``True``); whether to check + that `R` is a commutative ring. + + EXAMPLES:: + + sage: LieConformalAlgebras(QuaternionAlgebra(2)) + Traceback (most recent call last): + ValueError: base must be a commutative ring got Quaternion Algebra (-1, -1) with base ring Rational Field + sage: LieConformalAlgebras(ZZ) + Category of Lie conformal algebras over Integer Ring + """ + if check: + if not (R in _CommutativeRings): + raise ValueError("base must be a commutative ring got {}"\ + .format(R)) + return super(LieConformalAlgebras,cls).__classcall__(cls,R) + + @cached_method + def super_categories(self): + """ + The list of super categories of this category. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQ) + sage: C.super_categories() + [Category of vector spaces over Rational Field] + sage: C = LieConformalAlgebras(QQ).FinitelyGenerated(); C + Category of finitely generated Lie conformal algebras over Rational Field + sage: C.super_categories() + [Category of Lie conformal algebras over Rational Field] + sage: C.all_super_categories() + [Category of finitely generated Lie conformal algebras over Rational Field, + Category of Lie conformal algebras over Rational Field, + Category of vector spaces over Rational Field, + Category of modules over Rational Field, + Category of bimodules over Rational Field on the left and Rational Field on the right, + Category of right modules over Rational Field, + Category of left modules over Rational Field, + Category of commutative additive groups, + Category of additive groups, + Category of additive inverse additive unital additive magmas, + Category of commutative additive monoids, + Category of additive monoids, + Category of additive unital additive magmas, + Category of commutative additive semigroups, + Category of additive commutative additive magmas, + Category of additive semigroups, + Category of additive magmas, + Category of sets, + Category of sets with partial maps, + Category of objects] + """ + return [Modules(self.base_ring())] + + def example(self): + """ + An example of parent in this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).example() + The Virasoro Lie conformal algebra over Rational Field + """ + from sage.algebras.lie_conformal_algebras.examples import \ + VirasoroLieConformalAlgebra + return VirasoroLieConformalAlgebra(self.base_ring()) + + def _repr_object_names(self): + """ + The name of the objects of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ) + Category of Lie conformal algebras over Rational Field + sage: LieConformalAlgebras(QQ).Graded().FinitelyGenerated() + Category of finitely generated H-graded Lie conformal algebras over Rational Field + """ + return "Lie conformal algebras over {}".format(self.base_ring()) + + class ParentMethods: + + @abstract_method + def ideal(self, *gens, **kwds): + """ + The ideal of this conformal algebra generated by ``gens``. + + .. TODO:: + + Ideals of Lie Conformal Algebras are not implemented + yet. + """ + raise NotImplementedError("Ideals of Lie Conformal algebras are "\ + "not implemented yet") + + def is_super(self): + """ + Wether this Lie conformal algebra is a super Lie + conformal algebra. + + EXAMPLES:: + + sage: V = VirasoroLieConformalAlgebra(QQ) + sage: V.is_super() + False + sage: NeveuSchwarzLieConformalAlgebra(QQbar).is_super() + True + + Notice that we can force to have a *purely even* super Lie + conformal algebra:: + + sage: bosondict = {('a','a'):{1:{('K',0):1}}} + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',),central_elements=('K',),super=True) + sage: R.is_super() + True + sage: [g.is_even_odd() for g in R.gens()] + [0, 0] + """ + return self in LieConformalAlgebras(self.base_ring()).Super() + + def is_graded(self): + """ + Wether this Lie conformal algebra is graded or not + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir + The Virasoro Lie conformal algebra over Rational Field + sage: Vir.is_graded() + True + """ + return self in LieConformalAlgebras(self.base_ring()).Graded() + + class ElementMethods: + @coerce_binop + def bracket(self,rhs): + r""" + The `\lambda`-bracket of these two elements. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal Algebra:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L.bracket(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H.bracket(H) + {1: 2*B['K']} + sage: E.bracket(F) + {0: B[alphacheck[1]], 1: B['K']} + + With a super Lie Conformal Algebra:: + + sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: R.inject_variables() + Defining L, G, C + sage: G.bracket(G) + {0: 2*L, 2: 2/3*C} + + .. NOTE:: + + This method coerces both elements to the same parent + in order to implement a Lie conformal algebra the user + needs to implement :meth:`_bracket_` + """ + return self._bracket_(rhs) + + @abstract_method + def _bracket_(self,rhs): + r""" + Returns the `\lambda`-bracket of these two elements. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal Algebra:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: L._bracket_(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L._bracket_(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H._bracket_(H) + {1: 2*B['K']} + sage: E._bracket_(F) + {0: B[alphacheck[1]], 1: B['K']} + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + """ + raise NotImplementedError("Not implemented") + + @coerce_binop + def nproduct(self,rhs,n): + r""" + Returns the n-th product of these two elements. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: L.nproduct(L,3) + 1/2*C + sage: L.nproduct(L.T(),0) + 2*T^(2)L + sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E.nproduct(H,0) == - 2*E + True + sage: E.nproduct(F,1) + B['K'] + + .. NOTE:: + + This method coerces both elements to the same parent + in order to implement a Lie conformal algebra the user + needs to implement :meth:`_nproduct_` + """ + return self._nproduct_(rhs,n) + + def _nproduct_(self,rhs,n): + r""" + Returns the n-th product of this two elements. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: L._nproduct_(L,3) + 1/2*C + sage: L._nproduct_(L.T(),0) + 2*T^(2)L + sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E._nproduct_(H,0) == - 2*E + True + sage: E._nproduct_(F,1) + B['K'] + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + """ + if n >= 0: + return self.bracket(rhs).get(n,self.parent().zero()) + else: + raise ValueError("Vertex algebras are not implemented yet") + + @abstract_method(optional=False) + def T(self, n=1): + r""" + The n-th derivative of this element. + + INPUT: + + - ``n`` -- integer (default:``1``); how many times + to apply `T` to this element. + + OUTPUT: + + `T^n a` where `a` is this element. Notice that We use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}` + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.inject_variables() + Defining L, C + sage: L.T() + TL + sage: L.T(3) + 6*T^(3)L + sage: C.T() + 0 + """ + raise NotImplementedError("Not implemented") + + @abstract_method + def is_even_odd(self): + """ + Return ``0`` if this element is *even* and ``1`` if it is + *odd*. + + EXAMPLES:: + + sage: R = NeveuSchwarzLieConformalAlgebra(QQ); + sage: R.inject_variables() + Defining L, G, C + sage: G.is_even_odd() + 1 + """ + raise NotImplementedError("Not Implemented") + + class SubcategoryMethods: + + def FinitelyGeneratedAsLieConformalAlgebra(self): + """ + The subcategory of finitely generated Lie conformal + algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated() + Category of finitely generated Lie conformal algebras over Rational Field + """ + return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") + + + def FinitelyGenerated(self): + """ + The subcategory of finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated() + Category of finitely generated Lie conformal algebras over Rational Field + """ + return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") + + def WithBasis(self): + """ + The subcategory of Lie conformal algebras with a preferred + basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).WithBasis() + Category of Lie conformal algebras over Rational Field with basis + """ + return self._with_axiom("WithBasis") + + def Graded(self, base_ring=None): + """ + The subcategory of H-Graded Lie conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(ZZ).WithBasis().Graded(); C + Category of H-graded Lie conformal algebras with basis over Integer Ring + sage: D = LieConformalAlgebras(ZZ).Graded().WithBasis() + sage: D is C + True + """ + assert base_ring is None or base_ring is self.base_ring() + if isinstance(self, CategoryWithAxiom_over_base_ring): + axioms_whitelist = frozenset(["WithBasis", + "FinitelyGeneratedAsLieConformalAlgebra"]) + axioms = axioms_whitelist.intersection(self.axioms()) + return self.base_category().Graded()._with_axioms(axioms) + return GradedModulesCategory.category_of(self) + + def Super(self, base_ring=None): + """ + The subcategory of super Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).Super().WithBasis() + Category of super Lie conformal algebras with basis over Algebraic Real Field + """ + assert base_ring is None or base_ring is self.base_ring() + if isinstance(self,CategoryWithAxiom_over_base_ring): + axioms_whitelist = frozenset(["WithBasis", + "FinitelyGeneratedAsLieConformalAlgebra"]) + axioms = axioms_whitelist.intersection(self.axioms()) + return self.base_category().Super()._with_axioms(axioms) + return SuperModulesCategory.category_of(self) + + class Super(SuperModulesCategory): + """ + The subcategory of super Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).Super().WithBasis() + Category of super Lie conformal algebras with basis over Algebraic Real Field + """ + #Need to do all this to make Super commute with Graded. + def extra_super_categories(self): + """ + The extra super categories of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super().super_categories() + [Category of super H-graded Lie conformal algebras over Rational Field, + Category of finitely generated super Lie conformal algebras over Rational Field, + Category of finitely generated H-graded Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + def example(self): + """ + An example parent in this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).Super().example() + The Neveu-Schwarz super Lie conformal algebra over Rational Field + """ + from sage.algebras.lie_conformal_algebras.examples import \ + NeveuSchwarzLieConformalAlgebra + return NeveuSchwarzLieConformalAlgebra(self.base_ring()) + + class SubcategoryMethods: + + def Graded(self, base_ring=None): + """ + The subcategory of H-graded super Lie conformal + algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).Super().Graded() + Category of super H-graded Lie conformal algebras over Rational Field + """ + assert base_ring is None or base_ring is self.base_ring() + if isinstance(self,CategoryWithAxiom_over_base_ring): + axioms_whitelist = frozenset(["WithBasis", + "FinitelyGeneratedAsLieConformalAlgebra"]) + axioms = axioms_whitelist.intersection(self.axioms()) + return self.base_category().Graded()._with_axioms(axioms) + + return GradedModulesCategory.category_of( + self.base_category()).Super() + + + class WithBasis(CategoryWithAxiom_over_base_ring): + """ + The subcategory of Super Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Super().WithBasis() + Category of super Lie conformal algebras with basis over Integer Ring + sage: LieConformalAlgebras(ZZ).Super().WithBasis() is LieConformalAlgebras(ZZ).WithBasis().Super() + True + """ + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated super Lie + conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Super().FinitelyGenerated().WithBasis() + Category of finitely generated super Lie conformal algebras with basis over Integer Ring + """ + pass + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated super Lie + conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Super().FinitelyGenerated() + Category of finitely generated super Lie conformal algebras over Integer Ring + """ + pass + + + class Graded(GradedModulesCategory): + """ + The subcategory of H-graded Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field + """ + + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Super().Graded() + Category of super H-graded Lie conformal algebras over Algebraic Field + sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() + Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) + + + class SubcategoryMethods: + + def Super(self, base_ring=None): + """ + The subcategory of H-graded super Lie conformal algebras + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).Super().Graded() + Category of super H-graded Lie conformal algebras over Rational Field + """ + assert base_ring is None or base_ring is self.base_ring() + if isinstance(self,CategoryWithAxiom_over_base_ring): + axioms_whitelist = frozenset(["WithBasis", + "FinitelyGeneratedAsLieConformalAlgebra"]) + axioms = axioms_whitelist.intersection(self.axioms()) + return self.base_category().Super()._with_axioms(axioms) + return SuperModulesCategory.category_of(self) + + class WithBasis(CategoryWithAxiom_over_base_ring): + """ + The subcategory of H-graded Lie conformal algebras with + basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Graded().WithBasis() + Category of H-graded Lie conformal algebras with basis over Integer Ring + """ + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated H-graded + Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() + Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring + """ + pass + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated H-graded Lie + conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(ZZ).Graded().FinitelyGenerated(); C + Category of finitely generated H-graded Lie conformal algebras over Integer Ring + sage: C is LieConformalAlgebras(ZZ).FinitelyGenerated().Graded() + True + """ + pass + + class Super(SuperModulesCategory): + """ + The subcategory of H-graded super Lie conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar).Graded().Super(); C + Category of super H-graded Lie conformal algebras over Algebraic Field + sage: C is LieConformalAlgebras(QQbar).Super().Graded() + True + """ + def extra_super_categories(self): + """ + The extra super categories of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super().super_categories() + [Category of super H-graded Lie conformal algebras over Rational Field, + Category of finitely generated super Lie conformal algebras over Rational Field, + Category of finitely generated H-graded Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + + class ElementMethods: + @abstract_method + def degree(self): + """ + The degree of this element. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: L.degree() + 2 + sage: L.T(3).degree() + 5 + + sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: R.inject_variables() + Defining L, G, C + sage: G.degree() + 3/2 + """ + raise NotImplementedError("Not implemented") + + class WithBasis(CategoryWithAxiom_over_base_ring): + """ + The subcategory of Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis() + Category of Lie conformal algebras over Algebraic Field with basis + """ + def _repr_object_names(self): + """ + The names of objects of this category. + """ + return "{} with basis".format(self.base_category().\ + _repr_object_names()) + + class ElementMethods: + + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = NeveuSchwarzLieConformalAlgebra(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for "\ + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + + class SubcategoryMethods: + def FinitelyGenerated(self): + """ + The subcategory of finitely generated Lie conformal + algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() + Category of finitely generated Lie conformal algebras with basis over Algebraic Field + """ + return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated Lie conformal + algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() + Category of finitely generated Lie conformal algebras with basis over Algebraic Field + """ + pass + + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated Lie conformal + algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() + Category of finitely generated Lie conformal algebras with basis over Algebraic Field + """ + def _repr_object_names(self): + """ + The names of objects of this category. + """ + return "finitely generated {}".format(self.base_category().\ + _repr_object_names()) + + class ParentMethods: + def ngens(self): + r""" + The number of generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir.ngens() + 2 + + sage: V = AffineLieConformalAlgebra(QQ, 'A2') + sage: V.ngens() + 9 + + sage: L = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: L.gens() + (L, G, C) + sage: L.ngens() + 3 + """ + return len(self.gens()) + + def gen(self,i): + r""" + The i-th generator of this Lie conformal algebra. + + EXAMPLES:: + + sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: V.gen(0) + B[alpha[1]] + sage: V.1 + B[alphacheck[1]] + """ + return self.gens()[i] From 51dd905b51e8c3c6134430e4774fa3c90839e549 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 30 Jun 2020 12:34:17 -0700 Subject: [PATCH 034/379] sage.numerical.backends.generic_sdp_backend: Allow solver = a user-defined subclass of GenericSDPBackend --- .../backends/generic_sdp_backend.pyx | 75 +++++++++++++------ 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/src/sage/numerical/backends/generic_sdp_backend.pyx b/src/sage/numerical/backends/generic_sdp_backend.pyx index 0af4a9317a8..664eaf14877 100644 --- a/src/sage/numerical/backends/generic_sdp_backend.pyx +++ b/src/sage/numerical/backends/generic_sdp_backend.pyx @@ -605,29 +605,30 @@ cdef class GenericSDPBackend: default_solver = None -def default_sdp_solver(solver = None): +def default_sdp_solver(solver=None): """ - Returns/Sets the default SDP Solver used by Sage + Return/set the default SDP solver used by Sage INPUT: - - ``solver`` -- defines the solver to use: + - ``solver`` -- one of the following: + - the string ``"CVXOPT"``, to make the use of the CVXOPT solver + (see the `CVXOPT `_ web site) the default; - - CVXOPT (``solver="CVXOPT"``). See the `CVXOPT - `_ web site. + - a subclass of + :class:`sage.numerical.backends.generic_sdp_backend.GenericSDPBackend`, + to make it the default; or - ``solver`` should then be equal to one of ``"CVXOPT"``. - - - If ``solver=None`` (default), the current default solver's name is - returned. + - ``None`` (default), in which case the current default solver + (a string or a class) is returned. OUTPUT: - This function returns the current default solver's name if ``solver = None`` - (default). Otherwise, it sets the default solver to the one given. If this - solver does not exist, or is not available, a ``ValueError`` exception is - raised. + This function returns the current default solver (a string or a + class) if ``solver = None`` (default). Otherwise, it sets the + default solver to the one given. If this solver does not exist, or + is not available, a ``ValueError`` exception is raised. EXAMPLES:: @@ -638,8 +639,14 @@ def default_sdp_solver(solver = None): sage: default_sdp_solver("Yeahhhhhhhhhhh") Traceback (most recent call last): ... - ValueError: 'solver' should be set to 'CVXOPT' or None. + ValueError: 'solver' should be set to ... sage: default_sdp_solver(former_solver) + sage: from sage.numerical.backends.generic_sdp_backend import GenericSDPBackend + sage: class my_sdp_solver(GenericSDPBackend): pass + sage: default_sdp_solver(my_sdp_solver) + sage: default_sdp_solver() is my_sdp_solver + True + """ global default_solver @@ -656,8 +663,11 @@ def default_sdp_solver(solver = None): except ValueError: pass - solver = solver.capitalize() + if callable(solver): + default_solver = solver + return + solver = solver.capitalize() if solver == "Cvxopt": try: @@ -667,7 +677,7 @@ def default_sdp_solver(solver = None): raise ValueError("CVXOPT is not available. Please refer to the documentation to install it.") else: - raise ValueError("'solver' should be set to 'CVXOPT' or None.") + raise ValueError("'solver' should be set to 'CVXOPT', a class, or None.") cpdef GenericSDPBackend get_solver(solver = None): """ @@ -675,13 +685,16 @@ cpdef GenericSDPBackend get_solver(solver = None): INPUT: - - ``solver`` -- 1 solver should be available through this class: + - ``solver`` -- one of the following: + + - the string ``"CVXOPT"``, designating the use of the CVXOPT solver + (see the `CVXOPT `_ web site); - - CVXOPT (``solver="CVXOPT"``). See the `CVXOPT - `_ web site. + - a subclass of + :class:`sage.numerical.backends.generic_sdp_backend.GenericSDPBackend`; - ``solver`` should then be equal to one of ``"CVXOPT"`` or ``None``. - If ``solver=None`` (default), the default solver is used (see ``default_sdp_solver`` method. + - ``None`` (default), in which case the default solver is used (see + :func:`default_sdp_solver`); .. SEEALSO:: @@ -691,17 +704,31 @@ cpdef GenericSDPBackend get_solver(solver = None): sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver() + + Passing a class:: + + sage: from sage.numerical.backends.generic_sdp_backend import GenericSDPBackend + sage: class MockSDPBackend(GenericSDPBackend): + ....: def solve(self): + ....: raise RuntimeError("SDP is too slow!") + sage: P = SemidefiniteProgram(solver=MockSDPBackend) + sage: P.solve() + Traceback (most recent call last): + ... + RuntimeError: SDP is too slow! + """ if solver is None: solver = default_sdp_solver() - else: - solver = solver.capitalize() + if callable(solver): + return solver() + solver = solver.capitalize() if solver == "Cvxopt": from sage.numerical.backends.cvxopt_sdp_backend import CVXOPTSDPBackend return CVXOPTSDPBackend() else: - raise ValueError("'solver' should be set to 'CVXOPT' or None (in which case the default one is used).") + raise ValueError("'solver' should be set to 'CVXOPT', a class, or None (in which case the default one is used).") From a65791942da8a6d46e24223ad5372ee765cdcd3a Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Tue, 30 Jun 2020 21:12:39 -0300 Subject: [PATCH 035/379] Minor doctesting and bot complaints. --- .../lie_conformal_algebras/examples.py | 5 ++++ src/sage/categories/lie_conformal_algebras.py | 24 +++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/sage/algebras/lie_conformal_algebras/examples.py b/src/sage/algebras/lie_conformal_algebras/examples.py index f81381086f6..5a83a31c5b2 100644 --- a/src/sage/algebras/lie_conformal_algebras/examples.py +++ b/src/sage/algebras/lie_conformal_algebras/examples.py @@ -161,6 +161,11 @@ def cartan_type(self): def _repr_(self): """ The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: AffineLieConformalAlgebra(QQ, 'A1') + The affine Lie conformal algebra of type ['A', 1] over Rational Field """ return "The affine Lie conformal algebra of type {} over {}".format( self._ct,self.base_ring()) diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index c125cd503cc..34857b4fee9 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -361,7 +361,7 @@ def bracket(self,rhs): With a super Lie Conformal Algebra:: - sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); sage: R.inject_variables() Defining L, G, C sage: G.bracket(G) @@ -378,7 +378,7 @@ def bracket(self,rhs): @abstract_method def _bracket_(self,rhs): r""" - Returns the `\lambda`-bracket of these two elements. + The `\lambda`-bracket of these two elements. EXAMPLES: @@ -411,7 +411,7 @@ def _bracket_(self,rhs): @coerce_binop def nproduct(self,rhs,n): r""" - Returns the n-th product of these two elements. + The ``n``-th product of these two elements. EXAMPLES:: @@ -437,7 +437,7 @@ def nproduct(self,rhs,n): def _nproduct_(self,rhs,n): r""" - Returns the n-th product of this two elements. + The ``n``-th product of these two elements. EXAMPLES:: @@ -618,7 +618,7 @@ def example(self): from sage.algebras.lie_conformal_algebras.examples import \ NeveuSchwarzLieConformalAlgebra return NeveuSchwarzLieConformalAlgebra(self.base_ring()) - + class SubcategoryMethods: def Graded(self, base_ring=None): @@ -803,7 +803,7 @@ def degree(self): sage: L.T(3).degree() 5 - sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); sage: R.inject_variables() Defining L, G, C sage: G.degree() @@ -823,6 +823,11 @@ class WithBasis(CategoryWithAxiom_over_base_ring): def _repr_object_names(self): """ The names of objects of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).WithBasis() + Category of Lie conformal algebras over Rational Field with basis """ return "{} with basis".format(self.base_category().\ _repr_object_names()) @@ -896,6 +901,11 @@ class FinitelyGeneratedAsLieConformalAlgebra( def _repr_object_names(self): """ The names of objects of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated() + Category of finitely generated Lie conformal algebras over Rational Field """ return "finitely generated {}".format(self.base_category().\ _repr_object_names()) @@ -915,7 +925,7 @@ def ngens(self): sage: V.ngens() 9 - sage: L = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: L = NeveuSchwarzLieConformalAlgebra(QQbar); sage: L.gens() (L, G, C) sage: L.ngens() From a78e93918fab9837e4b15080c9253367ee5ecef7 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 1 Jul 2020 14:01:40 -0400 Subject: [PATCH 036/379] Initial commit --- src/sage/schemes/berkovich/berkovich_space.py | 314 ++++++++++++------ 1 file changed, 204 insertions(+), 110 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 69a9d0d91f3..999a646ced9 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -45,6 +45,8 @@ from sage.schemes.affine.affine_space import AffineSpace from sage.schemes.generic.scheme import Scheme from sage.rings.rational_field import QQ +from sage.rings.integer_ring import ZZ +from sage.rings.number_field.number_field import is_NumberField from sage.rings.infinity import Infinity def is_Berkovich(space): @@ -179,27 +181,34 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if flag: raise ValueError("the center of a point of Projective Berkovich" + \ "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) - elif not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(),pAdicBaseGeneric): - try: - center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of Projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s") %self._base_space + else: + if self._base_type == 'padic': + if not is_pAdicField(center.scheme().base_ring()): + if not isinstance(center.scheme().base_ring(),pAdicBaseGeneric): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("the center of a point of Projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s") %self._base_space + else: + try: + center = (self._base_space)(center) + except: + pass + if (center.scheme()).base_ring().prime() != self._p: + raise ValueError("the center of a disk approximating a Type IV point of Berkovich space" + \ + " over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) else: - try: - center = (self._base_space)(center) - except: - pass + base_field = self.parent().base_ring() + if not (center[0] in base_field and center[1] in base_field): + raise ValueError('center of a point of Berkovich space in projective space ' + \ + 'over the wrong field') if center.scheme().ambient_space() != center.scheme(): raise ValueError("the center of a point of Berkovich space over " + \ "P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) - if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("the center of a disk approximating a Type IV point of Berkovich space" + \ - " over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) if center == (center.scheme())((1,0)): raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ "space cannot be centered at %s" %((center.scheme())((1,0)))) @@ -226,25 +235,31 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] - #make sure the center is in Cp - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - self._center_lst[i] = center - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a disk approximating a Type IV point must " + \ - "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) - elif not is_pAdicField(center.parent()): - try: - center = (self._base_space)(center) - except: - pass - if (center.parent()).prime() != self._p: - raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ - "space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + if self._base_type == 'padic': + #make sure the center is in Cp + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + self._center_lst[i] = center + flag = False + except: + flag = True + if flag: + raise ValueError("the center of a disk approximating a Type IV point must " + \ + "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) + elif not is_pAdicField(center.parent()): + try: + center = (self._base_space)(center) + except: + pass + if (center.parent()).prime() != self._p: + raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ + "space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + else: + #make sure the center is in the appropriate number field + if not(center in self._base_space): + raise ValueError('center defined over %s passed to ' %center.parent()+ \ + 'Berkovich space defined over number field %s' %self._base_space) #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): @@ -272,54 +287,64 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, #the point must now be Type 1, 2, or 3, so we check that the center is of the appropriate type if error_check: if child == "affine": - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of Affine Berkovich space over " + \ - "%s must convert to %s" %(self._base_space,self._base_space)) - elif not is_pAdicField(center.parent()): - try: - center = (self._base_space)(center) - except: - pass - if (center.parent()).prime() != self._p: - raise ValueError("the center of a point of Berkovich space over " + \ - "Cp(%s) cannot be a point of %s" %(self._p, center.parent())) - elif child == "projective": - if not isinstance(center, SchemeMorphism_point_projective_field): - try: - center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of Projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s" %self._base_space) - elif not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): + if self._base_type == 'padic': + if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) flag = False except: flag = True if flag: - raise ValueError("the center of a point of Projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s") %self._base_space - else: + raise ValueError("the center of a point of Affine Berkovich space over " + \ + "%s must convert to %s" %(self._base_space,self._base_space)) + elif not is_pAdicField(center.parent()): try: center = (self._base_space)(center) except: pass + if (center.parent()).prime() != self._p: + raise ValueError("the center of a point of Berkovich space over " + \ + "Cp(%s) cannot be a point of %s" %(self._p, center.parent())) + else: + if not(center in self._base_space): + raise ValueError('center defined over %s passed to ' %center.parent()+ \ + 'Berkovich space defined over number field %s' %self._base_space) + elif child == "projective": + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("the center of a point of Projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s" %self._base_space) + if self._base_type == 'padic': + if not is_pAdicField(center.scheme().base_ring()): + if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("the center of a point of Projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s") %self._base_space + else: + try: + center = (self._base_space)(center) + except: + pass + if (center.scheme()).base_ring().prime() != self._p: + raise ValueError("the center of a point of Berkovich space over " + \ + "P^1(Cp(%s)) cannot be a point of %s" %(self._p, center.scheme())) + else: + if not(center[0] in self._base_space.base_ring() and center[1] in self._base_space.base_ring()): + raise ValueError('center defined over %s ' %center.parent() + \ + 'but Berkovich space defined over %s' %self._base_space) if not(center.scheme().ambient_space() is center.scheme()): raise ValueError("the center of a point of Projective Berkovich space cannot be " + \ "a point of %s" %(center.scheme())) - if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("the center of a point of Berkovich space over " + \ - "P^1(Cp(%s)) cannot be a point of %s" %(self._p, center.scheme())) else: raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ "Berkovich_Element_Cp directly" ) @@ -333,10 +358,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if child == "projective": try: center.dehomogenize(1) - flag = False except ValueError: - flag = True - if flag: raise ValueError("type II and III points cannot be centered at (1 : 0)") if power != None: if error_check: @@ -406,6 +428,14 @@ def prec(self): """ return self.precision() + def _custom_abs(self, x): + """ + Returns the absolute value of ``x`` with respect to the norm on ``Cp``. + + Used to simplify code, as ``x`` may be a point of a number field + or a padic field. + """ + def precision(self): """ Returns the precision of a Type IV point. @@ -925,9 +955,14 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() + self._base_type = parent._base_type + self._valuation = parent._valuation #if this is a point of projective berkovich space, we do the conversion if isinstance(center, Berkovich_Element_Cp_Projective): + if center._base_type != self._base_type: + raise ValueError('cannot convert from Berkovich space over ' + \ + 'a %s to Berkovich space over a %s field' %center._base_type, self._base_type) if (center.prime() == self._p): try: center.center().dehomogenize(1) @@ -1539,9 +1574,14 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() + self._base_type = parent._base_type + self._valuation = parent._valuation #conversion from Affine points is handled in this constructor if isinstance(center, Berkovich_Element_Cp_Affine): + if center._base_type != self._base_type: + raise ValueError('cannot convert from Berkovich space over ' + \ + 'a %s field to Berkovich space over a %s field' %(center._base_type, self._base_type)) if (center.prime() == self._p): self._center = (self._base_space)(center._center) self._radius = center._radius @@ -2250,17 +2290,33 @@ class Berkovich_Cp_Affine(Berkovich_Cp): Element = Berkovich_Element_Cp_Affine - def __init__(self,base): - from sage.rings.integer_ring import ZZ + def __init__(self, base, valuation=None): if base in ZZ: if base.is_prime(): base = Qp(base) #TODO chance to Qpbar else: raise ValueError("non-prime pased into Berkovich space") - if not is_pAdicField(base): #TODO change base to Qpbar(prime) - raise ValueError("base of Berkovich Space must be a padic field") - self._p = base.prime() - Parent.__init__(self, base = base, category=TopologicalSpaces()) + if is_NumberField(base): + if valuation == None: + raise ValueError('passed a number field but not a valuation') + from sage.rings.padics.padic_valuation import pAdicValuation_base + if not isinstance(valuation, pAdicValuation_base): + raise ValueError('valuation was not a padic valuation') + if valuation.domain() != base: + raise ValueError('passed number field ' + \ + '%s but valuation defined over %s' %(base, valuation.domain())) + prime = valuation.p() + self._base_type = 'number' + elif is_pAdicField(base): #TODO change base to Qpbar(prime) + prime = base.prime() + valuation = base.valuation() + self._base_type = 'padic' + else: + raise ValueError("base of Berkovich Space must be a padic field " + \ + "or a number field") + self._valuation = valuation + self._p = prime + Parent.__init__(self, base=base, category=TopologicalSpaces()) def residue_characteristic(self): """ @@ -2320,62 +2376,88 @@ class Berkovich_Cp_Projective(Berkovich_Cp): INPUT: - - ``base`` - The prime number `p`. Alternatively, can be a Projective Space over - `\QQ_p` or a finite extension of `\QQ_p`. This allows for more control of - conversion of centers. + - ``base`` -- Three cases: + + * a prime number ``p``. Centers of elements are then represented + as points of projective space of dimension 1 over `\QQ_p`. + + * `\QQ_p` or a finite extension of `\QQ_p`. Centers of elements + are then represented as points of projective space of dimension 1 + over ``base``. + + * A number field `K`. Centers of elements are then represented + as points of projective space of dimension 1 over ``base``. + + - ``valuation`` -- (optional) a valuation on ``base``. Must be + specified if a number field is passed to ``base``, otherwise + it is ignored. EXAMPLES:: sage: B = Berkovich_Cp_Projective(3); B Projective Berkovich line over Cp(3) of precision 20 - Initializing by passing in a projective space looks the same:: + Initializing by passing in a padic space looks the same:: - sage: S = ProjectiveSpace(Qp(3),1) - sage: B = Berkovich_Cp_Projective(S); B + sage: B = Berkovich_Cp_Projective(Qp(3)); B Projective Berkovich line over Cp(3) of precision 20 However, this method allows for more control over behind-the-scenes conversion:: - sage: S = ProjectiveSpace(Qp(3,1),1) + sage: S = Qp(3,1) sage: B = Berkovich_Cp_Projective(S); B Projective Berkovich line over Cp(3) of precision 1 sage: Q1 = B(1/2); Q1 Type I point centered at (2 + O(3) : 1 + O(3)) - Note that this point has very low precision, as S is a scheme over - `\QQ_3` of capped-relative precision 1. + Note that this point has very low precision, as S has low + precision cap. """ Element = Berkovich_Element_Cp_Projective - def __init__(self,base): - from sage.rings.integer_ring import ZZ + def __init__(self, base, valuation=None): if base in ZZ: if base.is_prime(): base = ProjectiveSpace(Qp(base),1) - Berkovich_Cp_Projective.__init__(self,base) - return else: raise ValueError("non-prime pased into Berkovich space") - elif is_pAdicField(base): + if is_NumberField(base): base = ProjectiveSpace(base, 1) - else: - from sage.schemes.projective.projective_space import is_ProjectiveSpace - if not is_ProjectiveSpace(base): - raise ValueError("base of Projective Berkovich Space must be Projective Space") - if not (is_pAdicField(base.base_ring())): - raise ValueError("base of Projective Berkovich Space must be " + \ - "Projective Space over Qp") - if base.ambient_space() != base: - raise ValueError("base of Projective Berkovich Space must be " + \ - "Projective Space over Qp") - if base.dimension() != 1: + if is_pAdicField(base): + base = ProjectiveSpace(base, 1) + from sage.schemes.projective.projective_space import is_ProjectiveSpace + if not is_ProjectiveSpace(base): + raise ValueError("base of Projective Berkovich Space must be Projective Space") + if not (is_pAdicField(base.base_ring())): + if not (is_NumberField(base.base_ring())): raise ValueError("base of Projective Berkovich Space must be " + \ - "Projective Space of dimension 1 over Qp") - self._p = base.base_ring().prime() + "Projective Space over Qp or a number field") + else: + if valuation == None: + raise ValueError('passed a number field but not a valuation') + from sage.rings.padics.padic_valuation import pAdicValuation_base + if not isinstance(valuation, pAdicValuation_base): + raise ValueError('valuation was not a padic valuation') + if valuation.domain() != base.base_ring(): + raise ValueError('passed number field ' + \ + '%s but valuation defined over %s' %(base.base_ring(), valuation.domain())) + prime = valuation.p() + self._base_type = 'number' + else: + valuation = base.base_ring().valuation() + prime = base.base_ring().prime() + self._base_type = 'padic' + if base.ambient_space() != base: + raise ValueError("base of Projective Berkovich Space must be " + \ + "Projective Space over Qp or a number field") + if base.dimension() != 1: + raise ValueError("base of Projective Berkovich Space must be " + \ + "Projective Space of dimension 1 over Qp or a number field") + self._p = prime + self._valuation = valuation Parent.__init__(self, base = base, category=TopologicalSpaces()) def residue_characteristic(self): @@ -2434,10 +2516,22 @@ def _repr_(self): sage: B = Berkovich_Cp_Projective(3) sage: B Projective Berkovich line over Cp(3) of precision 20 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^2+1) + sage: v = A.valuation(a+1) + sage: B = Berkovich_Cp_Projective(A,v); B + Projective Berkovich line over Cp(2), with base Number Field in a with defining polynomial x^2 + 1 """ - return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ - self.base().base_ring().precision_cap()) - + if self._base_type == 'padic': + return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ + self.base().base_ring().precision_cap()) + else: + return "Projective Berkovich line over Cp(%s), with base %s" %(self.prime(),\ + self.base().base_ring()) + def _latex_(self): r""" LaTeX representation of this Berkovich Space. From 498e433dcb4baa54032e5a4a5cfe992ef5b8750e Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Wed, 1 Jul 2020 21:45:14 -0300 Subject: [PATCH 037/379] Several Fixes to 30032 * Separated GradedLieConformalAlgebras() from the main category file * documented classes at the class level instead of __init__ * created catalog and moved examples to the lie_conformal_algebras namespace * got rid of NN * Added TestSuite calls to the __init__ methods * Added Parent to LieConformalAlgebra ancestors * Renamed LieConformalAlgebraWithGenerators to FreelyGeneratedLieConformalAlgebra --- .../algebras/lie_conformal_algebras.rst | 30 ++- src/doc/en/reference/categories/index.rst | 1 + .../affine_lie_conformal_algebra.py | 153 +++++++++++ .../algebras/lie_conformal_algebras/all.py | 8 +- .../lie_conformal_algebras/examples.py | 253 +----------------- ...initely_generated_lie_conformal_algebra.py | 36 ++- ...freely_generated_lie_conformal_algebra.py} | 38 +-- .../graded_lie_conformal_algebra.py | 98 +++---- .../lie_conformal_algebra.py | 34 ++- .../lie_conformal_algebra_element.py | 63 ++--- .../lie_conformal_algebra_with_basis.py | 59 ++-- ..._conformal_algebra_with_structure_coefs.py | 183 +++++++------ .../neveu_schwarz_lie_conformal_algebra.py | 81 ++++++ .../virasoro_lie_conformal_algebra.py | 79 ++++++ src/sage/categories/all.py | 2 +- .../graded_lie_conformal_algebras.py | 158 +++++++++++ src/sage/categories/lie_conformal_algebras.py | 216 +++------------ 17 files changed, 809 insertions(+), 683 deletions(-) create mode 100644 src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py rename src/sage/algebras/lie_conformal_algebras/{lie_conformal_algebra_with_generators.py => freely_generated_lie_conformal_algebra.py} (76%) create mode 100644 src/sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py create mode 100644 src/sage/categories/graded_lie_conformal_algebras.py diff --git a/src/doc/en/reference/algebras/lie_conformal_algebras.rst b/src/doc/en/reference/algebras/lie_conformal_algebras.rst index 357f08a33e8..d4c6c32f18f 100644 --- a/src/doc/en/reference/algebras/lie_conformal_algebras.rst +++ b/src/doc/en/reference/algebras/lie_conformal_algebras.rst @@ -1,18 +1,36 @@ Lie Conformal Algebras ====================== +The main classes to work with Lie conformal algebras +---------------------------------------------------- + .. toctree:: ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/examples ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element - ../sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra - ../sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra - ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis - ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators - ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs - .. SEEALSO:: :mod:`The Category of Lie Conformal Algebras` + +Implemented examples of Lie Conformal Algebras +---------------------------------------------- + +.. toctree:: + + ../sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra + + +See also +-------- + +.. toctree:: + + ../sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis + ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index 83771654981..b4fd9823dde 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -111,6 +111,7 @@ Individual Categories sage/categories/graded_hopf_algebras_with_basis sage/categories/graded_lie_algebras sage/categories/graded_lie_algebras_with_basis + sage/categories/graded_lie_conformal_algebras sage/categories/graded_modules sage/categories/graded_modules_with_basis sage/categories/graphs diff --git a/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py new file mode 100644 index 00000000000..2cd0e9fe2c4 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py @@ -0,0 +1,153 @@ +r""" +Affine Lie Conformal Algebra + +The affine Kac-Moody Lie conformal algebra associated to the +finite dimensional simple Lie algebra `\mathfrak{g}`. For a commutative +ring `R`, it is the `R[T]`-module freely generated by `\mathfrak{g}` +plus a central element `K` satisfying `TK = 0`. The non-vanishing +`\lambda`-brackets are given by + +.. MATH:: + + [a_\lambda b] = [a,b] + \lambda (a,b)K, + +where `a,b \in \mathfrak{g}` and `(a,b)` is the normalized +form of `\mathfrak{g}` so that its longest root has square-norm +`2`. + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.rings.integer import Integer +from sage.algebras.lie_algebras.lie_algebra import LieAlgebra +from .graded_lie_conformal_algebra import GradedLieConformalAlgebra + +class AffineLieConformalAlgebra(GradedLieConformalAlgebra): + r""" + The current or affine Kac-Moody Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative Ring; the base ring for this Lie + conformal algebra. + - ``ct`` -- a ``str`` or a :mod:`CartanType`; the Cartan Type for + the corresponding finite dimensional Lie algebra. It must + correspond to a simple finite dimensional Lie algebra. + - ``names`` -- a list of ``str`` or ``None`` (default: ``None``) + ; alternative names for the generators. If ``None`` the + generators are labeled by the corresponding root and coroot + vectors. + - ``prefix`` -- a ``str``; parameter passed to + :class:`IndexedGenerators` + - ``bracket`` -- a ``str``; parameter passed to + :class:`IndexedGenerators`. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.Affine(QQ, 'A1') + sage: R + The affine Lie conformal algebra of type ['A', 1] over Rational Field + sage: R.an_element() + B[alpha[1]] + B[alphacheck[1]] + B[-alpha[1]] + B['K'] + + sage: R = lie_conformal_algebras.Affine(QQ, 'A1', names = ('e', 'h','f')) + sage: R.inject_variables() + Defining e, h, f, K + sage: Family(e.bracket(f.T(3))) + Finite family {0: 6*T^(3)h, 1: 6*T^(2)h, 2: 6*Th, 3: 6*h, 4: 24*K} + + sage: V = lie_conformal_algebras.Affine(QQ, CartanType(["A",2,1])) + Traceback (most recent call last): + ... + ValueError: only affine algebras of simple finite dimensionalLie algebras are implemented + + OUTPUT: + + The Affine Lie conformal algebra associated with the finite + dimensional simple Lie algebra of Cartan type ``ct``. + """ + def __init__(self, R, ct, names=None, prefix=None, bracket=None): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.Affine(QQ,'A1') + sage: TestSuite(V).run() + """ + if type(ct) is str: + from sage.combinat.root_system.cartan_type import CartanType + try: + ct = CartanType(ct) + except IndexError: + raise ValueError("ct must be a valid Cartan Type") + if not (ct.is_finite() and ct.is_irreducible ): + raise ValueError("only affine algebras of simple finite dimensional" + "Lie algebras are implemented") + hv = Integer(ct.dual_coxeter_number()) + g = LieAlgebra(R, cartan_type=ct) + B = g.basis() + S = B.keys() + gdict = {} + for k1 in S: + for k2 in S: + if S.rank(k2) <= S.rank(k1): + myb = B[k1].bracket(B[k2]).monomial_coefficients() + myf = R(2).inverse_of_unit()*R(hv).inverse_of_unit()\ + *g.killing_form(B[k1],B[k2]) + if myb or myf: + gdict[(k1,k2)] = {} + if myb: + gdict[(k1,k2)][0] = {(nk,0):v for nk,v in \ + myb.items()} + if myf: + gdict[(k1,k2)][1] = {('K',0):myf} + + weights = (1,)*B.cardinality() + self._ct = ct + if prefix is None and names is None: + prefix = 'B' + + GradedLieConformalAlgebra.__init__(self, + R, gdict, index_set=S, + central_elements=('K',), weights=weights, + names=names, prefix=prefix,bracket=bracket) + + def cartan_type(self): + """ + The Cartan type of this Lie conformal algbera. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.Affine(QQ, 'B3') + sage: R + The affine Lie conformal algebra of type ['B', 3] over Rational Field + sage: R.cartan_type() + ['B', 3] + """ + return self._ct + + def _repr_(self): + """ + The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: lie_conformal_algebras.Affine(QQ, 'A1') + The affine Lie conformal algebra of type ['A', 1] over Rational Field + """ + return "The affine Lie conformal algebra of type {} over {}".format( + self._ct,self.base_ring()) diff --git a/src/sage/algebras/lie_conformal_algebras/all.py b/src/sage/algebras/lie_conformal_algebras/all.py index 239f8100e49..7d78e0a260c 100644 --- a/src/sage/algebras/lie_conformal_algebras/all.py +++ b/src/sage/algebras/lie_conformal_algebras/all.py @@ -10,9 +10,5 @@ from sage.misc.lazy_import import lazy_import -lazy_import('sage.algebras.lie_conformal_algebras.lie_conformal_algebra', -'LieConformalAlgebra') -lazy_import('sage.algebras.lie_conformal_algebras.examples', - ('AffineLieConformalAlgebra', - 'NeveuSchwarzLieConformalAlgebra', - 'VirasoroLieConformalAlgebra')) +lazy_import('sage.algebras.lie_conformal_algebras.lie_conformal_algebra','LieConformalAlgebra') +lazy_import('sage.algebras.lie_conformal_algebras', 'examples', 'lie_conformal_algebras') diff --git a/src/sage/algebras/lie_conformal_algebras/examples.py b/src/sage/algebras/lie_conformal_algebras/examples.py index 5a83a31c5b2..1ab88d15e2a 100644 --- a/src/sage/algebras/lie_conformal_algebras/examples.py +++ b/src/sage/algebras/lie_conformal_algebras/examples.py @@ -1,43 +1,12 @@ r""" Examples of Lie Conformal Algebras -- Affine Lie Conformal Algebra +We implement the following examples of Lie conformal algebras: - The affine Kac-Moody Lie conformal algebra associated to the - finite dimensional simple Lie algebra `\mathfrak{g}`. For a commutative - ring `R`, it is the `R[T]`-module freely generated by `\mathfrak{g}` - plus a central element `K` satisfying `TK = 0`. The non-vanishing - `\lambda`-brackets are given by - - .. MATH:: - - [a_\lambda b] = [a,b] + \lambda (a,b)K, - - where `a,b \in \mathfrak{g}` and `(a,b)` is the normalized - form of `\mathfrak{g}` so that its longest root has square-norm - `2`. - -- Virasoro Lie Conformal Algebra - - The Virasoro Lie conformal algebra is generated by `L` and a central - element `C`. The `\lambda`-brackets are given by: - - .. MATH:: - - [L_\lambda L] = T L + 2 \lambda L + \frac{\lambda^3}{12} C. - - It is an H-graded Lie conformal algebra with `L` of degree `2`. - -- Neveu-Schwarz Super Lie Conformal Algebra - - The `N=1` or *Neveu-Schwarz* super Lie conformal algebra is a super - extension of the Virasoro Lie conformal algebra with generators `L` - and `C` by an odd primary generator `G` of conformal weight `3/2`. The - remaining `\lambda`-bracket is given by: - - .. MATH:: - - [G_\lambda G] = 2L + \frac{\lambda^2}{3} C. +- :mod:`Affine Lie conformal algebra<.affine_lie_conformal_algebra>` +- :mod:`Neveu-Schwarz super Lie conformal + algebra<.neveu_schwarz_lie_conformal_algebra>` +- :mod:`Virasoro Lie conformal algebra<.virasoro_lie_conformal_algebra>` AUTHORS: @@ -45,7 +14,7 @@ """ #****************************************************************************** -# Copyright (C) 2019 Reimundo Heluani +# Copyright (C) 2020 Reimundo Heluani # # 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 @@ -54,210 +23,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from .graded_lie_conformal_algebra import GradedLieConformalAlgebra -from sage.rings.integer import Integer -from sage.algebras.lie_algebras.lie_algebra import LieAlgebra - -class AffineLieConformalAlgebra(GradedLieConformalAlgebra): - def __init__(self, R, ct, names=None, prefix=None, bracket=None): - r""" - The current or affine Kac-Moody Lie conformal algebra. - - INPUT: - - - ``R`` -- a commutative Ring; the base ring for this Lie - conformal algebra. - - ``ct`` -- a ``str`` or a :mod:`CartanType`; the Cartan Type for - the corresponding finite dimensional Lie algebra. It must - correspond to a simple finite dimensional Lie algebra. - - ``names`` -- a list of ``str`` or ``None`` (default: ``None``) - ; alternative names for the generators. If ``None`` the - generators are labeled by the corresponding root and coroot - vectors. - - ``prefix`` -- a ``str``; parameter passed to - :class:`IndexedGenerators<\ - sage.structure.indexed_generators.IndexedGenerators>` - - ``bracket`` -- a ``str``; parameter passed to - :class:`IndexedGenerators<\ - sage.structure.indexed_generators.IndexedGenerators>`. - - EXAMPLES:: - - sage: R = AffineLieConformalAlgebra(QQ, 'A1') - sage: R - The affine Lie conformal algebra of type ['A', 1] over Rational Field - sage: R.an_element() - B[alpha[1]] + B[alphacheck[1]] + B[-alpha[1]] + B['K'] - - sage: R = AffineLieConformalAlgebra(QQ, 'A1', names = ('e', 'h','f')) - sage: R.inject_variables() - Defining e, h, f, K - sage: Family(e.bracket(f.T(3))) - Finite family {0: 6*T^(3)h, 1: 6*T^(2)h, 2: 6*Th, 3: 6*h, 4: 24*K} - - sage: V = AffineLieConformalAlgebra(QQ, CartanType(["A",2,1])) - Traceback (most recent call last): - ... - ValueError: Only affine algebras of simple finite dimensionalLie algebras are implemented - - OUTPUT: - - The Affine Lie conformal algebra associated with the finite - dimensional simple Lie algebra of Cartan type ``ct``. - """ - if type(ct) is str: - from sage.combinat.root_system.cartan_type import CartanType - try: - ct = CartanType(ct) - except IndexError: - raise ValueError("ct must be a valid Cartan Type") - if not (ct.is_finite() and ct.is_irreducible ): - raise ValueError("Only affine algebras of simple finite dimensional" - "Lie algebras are implemented") - hv = Integer(ct.dual_coxeter_number()) - g = LieAlgebra(R, cartan_type=ct) - B = g.basis() - S = B.keys() - gdict = {} - for k1 in S: - for k2 in S: - if S.rank(k2) <= S.rank(k1): - myb = B[k1].bracket(B[k2]).monomial_coefficients() - myf = R(2).inverse_of_unit()*R(hv).inverse_of_unit()\ - *g.killing_form(B[k1],B[k2]) - if myb or myf: - gdict[(k1,k2)] = {} - if myb: - gdict[(k1,k2)][0] = {(nk,0):v for nk,v in \ - myb.items()} - if myf: - gdict[(k1,k2)][1] = {('K',0):myf} - - weights = (1,)*B.cardinality() - self._ct = ct - if prefix is None and names is None: - prefix = 'B' - - GradedLieConformalAlgebra.__init__(self, - R, gdict, index_set=S, - central_elements=('K',), weights=weights, - names=names, prefix=prefix,bracket=bracket) - - def cartan_type(self): - """ - The Cartan type of this Lie conformal algbera. - - EXAMPLES:: - - sage: R = AffineLieConformalAlgebra(QQ, 'B3') - sage: R - The affine Lie conformal algebra of type ['B', 3] over Rational Field - sage: R.cartan_type() - ['B', 3] - """ - return self._ct - - def _repr_(self): - """ - The name of this Lie conformal algebra. - - EXAMPLES:: - - sage: AffineLieConformalAlgebra(QQ, 'A1') - The affine Lie conformal algebra of type ['A', 1] over Rational Field - """ - return "The affine Lie conformal algebra of type {} over {}".format( - self._ct,self.base_ring()) - -class NeveuSchwarzLieConformalAlgebra(GradedLieConformalAlgebra): - - def __init__(self, R): - """ - The Neveu-Schwarz super Lie conformal algebra. - - INPUT: - - - ``R`` -- a commutative Ring; the base ring of this Lie - conformal algebra. - - EXAMPLES:: - - sage: R = NeveuSchwarzLieConformalAlgebra(AA); R - The Neveu-Schwarz super Lie conformal algebra over Algebraic Real Field - sage: R.structure_coefficients() - Finite family {('G', 'G'): ((0, 2*L), (2, 2/3*C)), ('G', 'L'): ((0, 1/2*TG), (1, 3/2*G)), ('L', 'G'): ((0, TG), (1, 3/2*G)), ('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} - sage: R.inject_variables() - Defining L, G, C - sage: G.nproduct(G,0) - 2*L - sage: G.degree() - 3/2 - """ - nsdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, - 3:{('C', 0):R(2).inverse_of_unit()}}, - ('L','G'):{0:{('G',1):1}, 1:{('G',0):R(3)*R(2).\ - inverse_of_unit()}}, ('G','G'): {0:{('L',0):2}, - 2:{('C',0):R(2)*R(3).inverse_of_unit()}}} - from sage.rings.rational_field import QQ - weights = (2,QQ(3/2)) - parity = (0,1) - GradedLieConformalAlgebra.__init__(self, R, nsdict, names=('L','G'), - central_elements=('C',), weights=weights, parity=parity) - - def _repr_(self): - """ - The name of this Lie Conformal algebra. - - EXAMPLES:: - - sage: R = NeveuSchwarzLieConformalAlgebra(GF(5)); R - The Neveu-Schwarz super Lie conformal algebra over Finite Field of size 5 - """ - return "The Neveu-Schwarz super Lie conformal algebra over {}".\ - format(self.base_ring()) - - - -class VirasoroLieConformalAlgebra(GradedLieConformalAlgebra): - def __init__(self, R): - """ - The Virasoro Lie Conformal algebra over `R`. - - INPUT: - - - ``R`` -- a commutative ring; behaviour is undefined if `R` is - not a Field of characteristic zero. - - EXAMPLES:: - - sage: Vir = VirasoroLieConformalAlgebra(QQ) - sage: Vir.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field - sage: Vir.inject_variables() - Defining L, C - sage: L.bracket(L) - {0: TL, 1: 2*L, 3: 1/2*C} - - TESTS:: - - sage: Vir.gens() - (L, C) - """ - virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, - 3:{('C', 0):R(2).inverse_of_unit()}}} - GradedLieConformalAlgebra.__init__(self,R, virdict, - names = ('L',), central_elements = ('C',), weights = (2,)) - - def _repr_(self): - """ - The name of this Lie conformal algebra. - - EXAMPLES:: - - sage: VirasoroLieConformalAlgebra(QQbar) - The Virasoro Lie conformal algebra over Algebraic Field - """ - return "The Virasoro Lie conformal algebra over {}".format( - self.base_ring()) - +from .affine_lie_conformal_algebra import AffineLieConformalAlgebra as Affine +from .neveu_schwarz_lie_conformal_algebra import NeveuSchwarzLieConformalAlgebra as NeveuSchwarz +from .virasoro_lie_conformal_algebra import VirasoroLieConformalAlgebra as Virasoro diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py index 872754cf816..22b96a3a88c 100644 --- a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py @@ -18,19 +18,27 @@ from sage.misc.cachefunc import cached_method from sage.categories.lie_conformal_algebras import LieConformalAlgebras -from .lie_conformal_algebra_with_generators import \ - LieConformalAlgebraWithGenerators +from .freely_generated_lie_conformal_algebra import \ + FreelyGeneratedLieConformalAlgebra -class FinitelyGeneratedLieConformalAlgebra(LieConformalAlgebraWithGenerators): +class FinitelyGeneratedLieConformalAlgebra(FreelyGeneratedLieConformalAlgebra): + """ + Abstract base class for finitely generated Lie conformal + algebras. + + This class provides minimal functionality, simply sets the + number of generators. + """ def __init__(self, R, index_set=None, central_elements=None, category=None, element_class=None, prefix=None, names=None, latex_names=None, **kwds): """ - Abstract base class for finitely generated Lie conformal - algebras. + Initialize self. + + TESTS:: - This class provides minimal functionality, simply sets the - number of generators. + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: TestSuite(V).run() """ category = LieConformalAlgebras(R).FinitelyGenerated().or_subcategory( category) @@ -69,7 +77,7 @@ def _an_element_(self): EXAMPLES:: - sage: R = NeveuSchwarzLieConformalAlgebra(QQ); R.an_element() + sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); R.an_element() L + G + C """ return self.sum(self.gens()) @@ -80,9 +88,9 @@ def ngens(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); Vir.ngens() + sage: Vir = lie_conformal_algebras.Virasoro(QQ); Vir.ngens() 2 - sage: V = AffineLieConformalAlgebra(QQ, 'A1'); V.ngens() + sage: V = lie_conformal_algebras.Affine(QQ, 'A1'); V.ngens() 4 """ return self._ngens @@ -99,14 +107,14 @@ def gens(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); + sage: Vir = lie_conformal_algebras.Virasoro(QQ); sage: Vir.gens() (L, C) .. SEEALSO:: :meth:`lie_conformal_algebra_generators<\ - LieConformalAlgebraWithGenerators.\ + FreelyGeneratedLieConformalAlgebra.\ lie_conformal_algebra_generators>` """ return self.lie_conformal_algebra_generators() @@ -118,7 +126,7 @@ def central_elements(self): EXAMPLES:: - sage: R = NeveuSchwarzLieConformalAlgebra(QQ); R.central_elements() + sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); R.central_elements() (C,) """ - return tuple(LieConformalAlgebraWithGenerators.central_elements(self)) + return tuple(FreelyGeneratedLieConformalAlgebra.central_elements(self)) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py b/src/sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py similarity index 76% rename from src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py rename to src/sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py index 5d2133c6809..d936fcb9f8a 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_generators.py +++ b/src/sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py @@ -1,5 +1,5 @@ """ -Lie Conformal Algebras With Generators. +Freely Generated Lie Conformal Algebras AUTHORS: @@ -23,20 +23,28 @@ from sage.sets.family import Family from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets -class LieConformalAlgebraWithGenerators(LieConformalAlgebraWithBasis): +class FreelyGeneratedLieConformalAlgebra(LieConformalAlgebraWithBasis): + """ + Base class for a central extension of a freely generated Lie + conformal algebra. + + This class provides minimal functionality, it sets up the + family of Lie conformal algebra generators. + + .. NOTE:: + + We now only accept direct sums of free modules plus + some central generators `C_i` such that `TC_i = 0`. + """ def __init__(self,R, index_set=None, central_elements=None, category=None, element_class=None, prefix=None, **kwds): """ - Base class for a Lie conformal algebra with distinguished - generators. - - This class provides minimal functionality, it sets up the - family of Lie conformal algebra generators. + Initialize self. - .. NOTE:: + TESTS:: - We now only accept direct sums of free modules plus - some central generators `C_i` such that `TC_i = 0`. + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: TestSuite(V).run() """ self._generators = Family(index_set) E = cartesian_product([index_set, NonNegativeIntegers()]) @@ -46,7 +54,7 @@ def __init__(self,R, index_set=None, central_elements=None, category=None, E = DisjointUnionEnumeratedSets((cartesian_product([ Family(central_elements), {Integer(0)}]),E)) - super(LieConformalAlgebraWithGenerators,self).__init__(R, basis_keys=E, + super(FreelyGeneratedLieConformalAlgebra,self).__init__(R, basis_keys=E, element_class=element_class, category=category, prefix=prefix, **kwds) @@ -64,10 +72,10 @@ def lie_conformal_algebra_generators(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.lie_conformal_algebra_generators() (L, C) - sage: V = AffineLieConformalAlgebra(QQ,'A1') + sage: V = lie_conformal_algebras.Affine(QQ,'A1') sage: V.lie_conformal_algebra_generators() (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) """ @@ -85,10 +93,10 @@ def central_elements(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.central_elements() (C,) - sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: V.central_elements() (B['K'],) """ diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py index 011152faedd..2c903224c0f 100644 --- a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -52,56 +52,64 @@ LieConformalAlgebraWithStructureCoefficients class GradedLieConformalAlgebra(LieConformalAlgebraWithStructureCoefficients): + r""" + An H-Graded Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring (default: ``None``); the base + ring of this Lie conformal algebra. Behaviour is undefined if + it is not a field of characteristic zero + + - ``s_coeff`` -- a dictionary (default: ``None``); as in the + input of :class:`LieConformalAlgebra` + + - ``names`` -- tuple of ``str`` (default: ``None``); as in the + input of :class:`LieConformalAlgebra` + + - ``central_elements`` -- tuple of ``str`` (default: ``None``); + as in the input of :class:`LieConformalAlgebra` + + - ``index_set`` -- enumerated set (default: ``None``); as in the + input of :class:`LieConformalAlgebra` + + - ``weights`` -- tuple of non-negative rational numbers + (default: tuple of ``1``); a list of degrees for this Lie + conformal algebra. + This tuple needs to have the same cardinality as + ``index_set`` or ``names``. Central elements are assumed + to have weight ``0``. + + - ``category`` The category that this Lie conformal algebra + belongs to. + + - ``parity`` -- tuple of ``0`` or ``1`` (Default: tuple of + ``0``); a tuple specifying the parity of each non-central + generator. + + EXAMPLES:: + + sage: bosondict = {('a','a'):{1:{('K',0):1}}} + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',),central_elements=('K',), weights=(1,)) + sage: R.inject_variables() + Defining a, K + sage: a.T(3).degree() + 4 + sage: K.degree() + 0 + sage: R.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + """ def __init__(self, R, s_coeff, index_set=None, central_elements=None, category=None, prefix=None, names=None, latex_names=None, parity=None, weights=None, **kwds): - r""" - An H-Graded Lie conformal algebra. - - INPUT: - - - ``R`` -- a commutative ring (default: ``None``); the base - ring of this Lie conformal algebra. Behaviour is undefined if - it is not a field of characteristic zero - - - ``s_coeff`` -- a dictionary (default: ``None``); as in the - input of :class:`LieConformalAlgebra` - - - ``names`` -- tuple of ``str`` (default: ``None``); as in the - input of :class:`LieConformalAlgebra` - - - ``central_elements`` -- tuple of ``str`` (default: ``None``); - as in the input of :class:`LieConformalAlgebra` - - - ``index_set`` -- enumerated set (default: ``None``); as in the - input of :class:`LieConformalAlgebra` - - - ``weights`` -- tuple of non-negative rational numbers - (default: tuple of ``1``); a list of degrees for this Lie - conformal algebra. - This tuple needs to have the same cardinality as - ``index_set`` or ``names``. Central elements are assumed - to have weight ``0``. - - - ``category`` The category that this Lie conformal algebra - belongs to. - - - ``parity`` -- tuple of ``0`` or ``1`` (Default: tuple of - ``0``); a tuple specifying the parity of each non-central - generator. + """ + Initialize self. - EXAMPLES:: + TESTS:: - sage: bosondict = {('a','a'):{1:{('K',0):1}}} - sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',),central_elements=('K',), weights=(1,)) - sage: R.inject_variables() - Defining a, K - sage: a.T(3).degree() - 4 - sage: K.degree() - 0 - sage: R.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: TestSuite(V).run() """ category = LieConformalAlgebras(R).Graded().WithBasis()\ .FinitelyGenerated().or_subcategory(category) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py index a3927cddf5e..2d7e1c7e519 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -110,7 +110,7 @@ .. SEEALSO:: - :mod:`sage.algebras.lie_conformal_algebras.examples` + :mod:`sage.algebras.lie_conformal_algebras.examples` The base class for all Lie conformal algebras is :class:`LieConformalAlgebra`. @@ -118,12 +118,16 @@ This class provides no functionality besides calling the appropriate constructor. +We provide some convenience classes to define named Lie conformal +algebras. See +:mod:`sage.algebras.lie_conformal_algebras.examples`. + EXAMPLES: - We construct the Virasoro Lie conformal algebra, its universal enveloping vertex algebra and lift some elements:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.inject_variables() Defining L, C sage: L.bracket(L) @@ -131,7 +135,7 @@ - We construct the Current algebra for `\mathfrak{sl}_2`:: - sage: R = AffineLieConformalAlgebra(QQ, 'A1', names = ('e', 'h', 'f')) + sage: R = lie_conformal_algebras.Affine(QQ, 'A1', names = ('e', 'h', 'f')) sage: R.gens() (e, h, f, K) sage: R.inject_variables() @@ -172,8 +176,9 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.sets.family import Family from sage.categories.commutative_rings import CommutativeRings +from sage.structure.parent import Parent -class LieConformalAlgebra(UniqueRepresentation): +class LieConformalAlgebra(UniqueRepresentation, Parent): r""" Lie Conformal Algebras base class and factory. @@ -258,8 +263,7 @@ class LieConformalAlgebra(UniqueRepresentation): .. Note:: Any remaining keyword is currently passed to - :class:`CombinatorialFreeModule`. + :class:`CombinatorialFreeModule`. EXAMPLES: @@ -306,18 +310,25 @@ class LieConformalAlgebra(UniqueRepresentation): def __classcall_private__(cls, R=None, arg0=None, index_set=None, central_elements=None, category=None, prefix=None, names=None, latex_names=None, parity=None, weights=None, **kwds): + """ + Lie conformal algebra factory. + + EXAMPLES:: + sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} + sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) + sage: type(V) + + """ if not R in CommutativeRings(): - raise ValueError("First argument must be a commutative ring" + - " got {}".format(R)) + raise ValueError("arg0 must be a commutative ring got {}".format(R)) #This is the only exposed class so we clean keywords here known_keywords = ['category', 'prefix', 'bracket', 'latex_bracket', 'string_quotes', 'sorting_key', 'graded', 'super'] for key in kwds: if key not in known_keywords: - raise ValueError("LieConformalAlgebra(): got an unexpected " + - "keyword argument '%s'"%key) + raise ValueError("got an unexpected keyword argument '%s'"%key) if isinstance(arg0,dict) and arg0: graded=kwds.pop("graded", False) @@ -337,5 +348,4 @@ def __classcall_private__(cls, R=None, arg0=None, index_set=None, central_elements=central_elements, category=category, prefix=prefix, names=names, latex_names=latex_names, parity=parity, **kwds) - return NotImplementedError("Not implemented") - + raise NotImplementedError("not implemented") diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index de270aa1a74..9a02badc6c8 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -19,6 +19,7 @@ from sage.functions.other import factorial from sage.misc.misc_c import prod +from sage.combinat.partition import Partition from sage.misc.misc import repr_lincomb from sage.misc.latex import latex from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement @@ -42,7 +43,7 @@ def T(self,n=1): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.inject_variables() Defining L, C sage: L.T() @@ -52,22 +53,13 @@ def T(self,n=1): sage: C.T() 0 - sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); R.inject_variables() + sage: R = lie_conformal_algebras.NeveuSchwarz(QQbar); R.inject_variables() Defining L, G, C sage: (L + 2*G.T() + 4*C).T(2) 2*T^(2)L + 12*T^(3)G - - TESTS:: - - sage: R = VirasoroLieConformalAlgebra(QQ); R.zero().T() - 0 - sage: (R.zero() + R.0).T() - TL - sage: R.0.T(0) - L """ - from sage.rings.all import NN - if n not in NN: + from sage.rings.all import ZZ + if n not in ZZ or n < 0: raise ValueError("n must be a nonnegative Integer") if n == 0 or self.is_zero(): return self @@ -89,20 +81,11 @@ def is_monomial(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: (L + L.T()).is_monomial() False sage: L.T().is_monomial() True - - TESTS:: - - sage: R = AffineLieConformalAlgebra(AA,'A1',names=('e','h','f')); R.inject_variables() - Defining e, h, f, K - sage: R.zero().is_monomial() - True - sage: (e.T(2) + R.zero()).is_monomial() - True """ return len(self._monomial_coefficients) == 1 or self.is_zero() @@ -119,7 +102,7 @@ def is_even_odd(self): EXAMPLES:: - sage: R = NeveuSchwarzLieConformalAlgebra(QQ); R.inject_variables() + sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); R.inject_variables() Defining L, G, C sage: L.is_even_odd() 0 @@ -144,13 +127,13 @@ def _bracket_(self, right): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L.bracket(L) {0: TL, 1: 2*L, 3: 1/2*C} sage: L.T().bracket(L) {1: -TL, 2: -4*L, 4: -2*C} - sage: R = AffineLieConformalAlgebra(QQbar, 'A1', names=('e','h','f')); R + sage: R = lie_conformal_algebras.Affine(QQbar, 'A1', names=('e','h','f')); R The affine Lie conformal algebra of type ['A', 1] over Algebraic Field sage: R.inject_variables() Defining e, h, f, K @@ -197,7 +180,7 @@ def _repr_(self): EXAMPLES:: - sage: V = VirasoroLieConformalAlgebra(QQ); V.inject_variables() + sage: V = lie_conformal_algebras.Virasoro(QQ); V.inject_variables() Defining L, C sage: v = L.T(5).nproduct(L,6); v -1440*L @@ -206,7 +189,7 @@ def _repr_(self): sage: L.T(4) 24*T^(4)L - sage: R = AffineLieConformalAlgebra(QQ, 'B3') + sage: R = lie_conformal_algebras.Affine(QQ, 'B3') sage: R.2.T()+3*R.3 TB[alpha[1]] + 3*B[alpha[2] + alpha[3]] """ @@ -237,26 +220,24 @@ def _latex_(self): EXAMPLES:: - sage: V = VirasoroLieConformalAlgebra(QQ); V.inject_variables() + sage: V = lie_conformal_algebras.Virasoro(QQ); V.inject_variables() Defining L, C sage: latex(L.T(2)) 2T^{(2)}L - sage: R = AffineLieConformalAlgebra(QQbar, 'A1', names=('e','h','f')); R.inject_variables() + sage: R = lie_conformal_algebras.Affine(QQbar, 'A1', names=('e','h','f')); R.inject_variables() Defining e, h, f, K sage: latex(e.bracket(f)) \left\{0 : h, 1 : K\right\} sage: latex(e.T(3)) 6T^{(3)}e - sage: R = AffineLieConformalAlgebra(QQbar, 'A1') + sage: R = lie_conformal_algebras.Affine(QQbar, 'A1') sage: latex(R.0.bracket(R.2)) \left\{0 : \alpha^\vee_{1}, 1 : \text{\texttt{K}}\right\} - - sage: R = AffineLieConformalAlgebra(QQ, 'A1'); latex(R.0.T(3)) + sage: R = lie_conformal_algebras.Affine(QQ, 'A1'); latex(R.0.T(3)) 6T^{(3)}\alpha_{1} - """ if self.is_zero(): return "0"; @@ -291,23 +272,13 @@ def degree(self): EXAMPLES:: - sage: V = VirasoroLieConformalAlgebra(QQ) + sage: V = lie_conformal_algebras.Virasoro(QQ) sage: V.inject_variables() Defining L, C sage: C.degree() 0 sage: L.T(4).degree() 6 - - sage: N = NeveuSchwarzLieConformalAlgebra(AA) - sage: N.inject_variables() - Defining L, G, C - sage: G.T().degree() - 5/2 - sage: L.degree() - 2 - sage: N.zero().degree() - +Infinity """ if self.is_zero(): from sage.rings.infinity import Infinity @@ -321,4 +292,4 @@ def degree(self): ls.append(ret) if ls[1:] == ls[:-1]: return ls[0] - raise ValueError("{} is not homogeneous!".format(self)) + raise ValueError("{} is not homogeneous".format(self)) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py index 340813221b2..af574531100 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py @@ -20,38 +20,47 @@ from sage.combinat.free_module import CombinatorialFreeModule class LieConformalAlgebraWithBasis(CombinatorialFreeModule): - def __init__(self,R, basis_keys=None, element_class=None, category=None, - prefix=None, **kwds): - """ - Abstract base class for a Lie conformal algebra with a - preferred basis. + """ + Abstract base class for a Lie conformal algebra with a + preferred basis. - This class provides no functionality, it simply passes the - arguments to :class:`CombinatorialFreeModule`. + This class provides no functionality, it simply passes the + arguments to :class:`CombinatorialFreeModule`. - EXAMPLES:: + EXAMPLES:: - sage: R = VirasoroLieConformalAlgebra(QQbar);R - The Virasoro Lie conformal algebra over Algebraic Field + sage: R = lie_conformal_algebras.Virasoro(QQbar);R + The Virasoro Lie conformal algebra over Algebraic Field + + TESTS:: + + sage: R = lie_conformal_algebras.Virasoro(QQ) + sage: R.0 + L + sage: R._repr_generator(R.0) + 'L' + sage: R = lie_conformal_algebras.Affine(QQ, 'A1') + sage: R.0 + B[alpha[1]] + sage: R._repr_generator(R.0) + 'B[alpha[1]]' + sage: R = lie_conformal_algebras.Affine(QQ, 'A1', names = ('e', 'h','f')) + sage: R.0 + e + sage: R._repr_generator(R.0) + 'e' + """ + def __init__(self,R, basis_keys=None, element_class=None, category=None, + prefix=None, **kwds): + """ + Initialize self. TESTS:: - sage: R = VirasoroLieConformalAlgebra(QQ) - sage: R.0 - L - sage: R._repr_generator(R.0) - 'L' - sage: R = AffineLieConformalAlgebra(QQ, 'A1') - sage: R.0 - B[alpha[1]] - sage: R._repr_generator(R.0) - 'B[alpha[1]]' - sage: R = AffineLieConformalAlgebra(QQ, 'A1', names = ('e', 'h','f')) - sage: R.0 - e - sage: R._repr_generator(R.0) - 'e' + sage: V = lie_conformal_algebras.Affine(QQ,'A1') + sage: TestSuite(V).run() """ + if prefix is None: prefix = '' kwds['bracket'] = '' diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py index c1f74e9b09c..f8b0028e335 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -30,6 +30,90 @@ class LieConformalAlgebraWithStructureCoefficients( FinitelyGeneratedLieConformalAlgebra): + r""" + A Lie conformal algebra with a set of specified structure + coefficients. + + INPUT: + + - ``R`` -- a ring (Default: ``None``); The base ring of this Lie + conformal algebra. Behaviour is undefined if it is not a field + of characteristic zero. + + - ``s_coeff`` -- Dictionary (Default: ``None``); + a dictionary containing the `\lambda` brackets of the + generators of this Lie conformal algebra. The family encodes a + dictionary whose keys + are pairs of either names or indices of the generators + and the values are themselves dictionaries. For a pair of + generators `a` and `b`, the value of ``s_coeff[('a','b')]`` is + a dictionary whose keys are positive integer numbers and the + corresponding value for the key `j` is a dictionary itself + representing the j-th product `a_{(j)}b`. + Thus, for a positive integer number `j`, the value of + ``s_coeff[('a','b')][j]`` is a dictionary whose entries are + pairs ``('c',n)`` where ``'c'`` is the name of a generator + and `n` is a positive number. The value for this key is the + coefficient of `\frac{T^{n}}{n!} c` in `a_{(j)}b`. For example + the ``s_coeff`` for the *Virasoro* Lie conformal algebra is:: + + {('L','L'):{0:{('L',1):1}, 1:{('L',0):2}, 3:{('C',0):1/2}}} + + + Do not include central elements in this dictionary. Also, if + the key ``('a','b')`` is present, there is no need to include + ``('b','a')`` as it is defined by skew-symmetry. + Any missing pair (besides the ones + defined by skew-symmetry) is assumed to have vanishing + `\lambda`-bracket. + + - ``names`` -- tuple of ``str`` (Default: ``None``); The list of + names for generators of this Lie conformal algebra. Do not + include central elements in this list. + + - ``central_elements`` -- tuple of ``str`` (Default: ``None``); + A list of names for central + elements of this Lie conformal algebra. + + - ``index_set`` -- enumerated set (Default: ``None``); + an indexing set for the generators of this Lie + conformal algebra. Do not include central elements in this + list. + + - ``parity`` -- tuple of `0` or `1` (Default: tuple of `0`); + a tuple specifying the parity of each non-central generator. + + EXAMPLES: + + - We construct the `\beta-\gamma` system by directly giving the + `\lambda`-brackets of the generators:: + + sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} + sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) + sage: V.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: V.inject_variables() + Defining a, b, K + sage: a.bracket(b) + {0: -K} + + - We construct the centerless Virasoro Lie conformal algebra:: + + sage: virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}}} + sage: R = LieConformalAlgebra(QQbar, virdict, names='L') + sage: R.inject_variables() + Defining L + sage: L.bracket(L) + {0: TL, 1: 2*L} + + - The construction checks that skew-symmetry is violated:: + + sage: wrongdict = {('L','L'):{0:{('L',1):2}, 1:{('L',0): 2}}} + sage: LieConformalAlgebra(QQbar, wrongdict, names='L') + Traceback (most recent call last): + ... + ValueError: two distinct values given for one and the same bracket. Skew-symmetry is not satisfied? + """ @staticmethod def _standardize_s_coeff(s_coeff, index_set, ce, parity=None): """ @@ -118,92 +202,15 @@ def _standardize_s_coeff(s_coeff, index_set, ce, parity=None): def __init__(self, R, s_coeff, index_set=None, central_elements=None, category=None, element_class=None, prefix=None, names=None, latex_names=None, parity=None, **kwds): - r""" - A Lie conformal algebra with a set of specified structure - coefficients. + names, index_set = standardize_names_index_set(names,index_set) + """ + Initialize self. - INPUT: + TESTS:: - - ``R`` -- a ring (Default: ``None``); The base ring of this Lie - conformal algebra. Behaviour is undefined if it is not a field - of characteristic zero. - - - ``s_coeff`` -- Dictionary (Default: ``None``); - a dictionary containing the `\lambda` brackets of the - generators of this Lie conformal algebra. The family encodes a - dictionary whose keys - are pairs of either names or indices of the generators - and the values are themselves dictionaries. For a pair of - generators `a` and `b`, the value of ``s_coeff[('a','b')]`` is - a dictionary whose keys are positive integer numbers and the - corresponding value for the key `j` is a dictionary itself - representing the j-th product `a_{(j)}b`. - Thus, for a positive integer number `j`, the value of - ``s_coeff[('a','b')][j]`` is a dictionary whose entries are - pairs ``('c',n)`` where ``'c'`` is the name of a generator - and `n` is a positive number. The value for this key is the - coefficient of `\frac{T^{n}}{n!} c` in `a_{(j)}b`. For example - the ``s_coeff`` for the *Virasoro* Lie conformal algebra is:: - - {('L','L'):{0:{('L',1):1}, 1:{('L',0):2}, 3:{('C',0):1/2}}} - - - Do not include central elements in this dictionary. Also, if - the key ``('a','b')`` is present, there is no need to include - ``('b','a')`` as it is defined by skew-symmetry. - Any missing pair (besides the ones - defined by skew-symmetry) is assumed to have vanishing - `\lambda`-bracket. - - - ``names`` -- tuple of ``str`` (Default: ``None``); The list of - names for generators of this Lie conformal algebra. Do not - include central elements in this list. - - - ``central_elements`` -- tuple of ``str`` (Default: ``None``); - A list of names for central - elements of this Lie conformal algebra. - - - ``index_set`` -- enumerated set (Default: ``None``); - an indexing set for the generators of this Lie - conformal algebra. Do not include central elements in this - list. - - - ``parity`` -- tuple of `0` or `1` (Default: tuple of `0`); - a tuple specifying the parity of each non-central generator. - - EXAMPLES: - - - We construct the `\beta-\gamma` system by directly giving the - `\lambda`-brackets of the generators:: - - sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} - sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) - sage: V.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field - sage: V.inject_variables() - Defining a, b, K - sage: a.bracket(b) - {0: -K} - - - We construct the centerless Virasoro Lie conformal algebra:: - - sage: virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}}} - sage: R = LieConformalAlgebra(QQbar, virdict, names='L') - sage: R.inject_variables() - Defining L - sage: L.bracket(L) - {0: TL, 1: 2*L} - - - The construction checks that skew-symmetry is violated:: - - sage: wrongdict = {('L','L'):{0:{('L',1):2}, 1:{('L',0): 2}}} - sage: LieConformalAlgebra(QQbar, wrongdict, names='L') - Traceback (most recent call last): - ... - ValueError: two distinct values given for one and the same bracket. Skew-symmetry is not satisfied? + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: TestSuite(V).run() """ - names, index_set = standardize_names_index_set(names,index_set) - if central_elements is None: central_elements= tuple() @@ -274,11 +281,11 @@ def structure_coefficients(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(AA) + sage: Vir = lie_conformal_algebras.Virasoro(AA) sage: Vir.structure_coefficients() Finite family {('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} - sage: NeveuSchwarzLieConformalAlgebra(QQ).structure_coefficients() + sage: lie_conformal_algebras.NeveuSchwarz(QQ).structure_coefficients() Finite family {('G', 'G'): ((0, 2*L), (2, 2/3*C)), ('G', 'L'): ((0, 1/2*TG), (1, 3/2*G)), ('L', 'G'): ((0, TG), (1, 3/2*G)), ('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} """ return self._s_coeff @@ -294,13 +301,13 @@ def _repr_generator(self, x): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQbar) + sage: Vir = lie_conformal_algebras.Virasoro(QQbar) sage: Vir._repr_generator(Vir.0) 'L' - sage: R = AffineLieConformalAlgebra(QQ, 'A1') + sage: R = lie_conformal_algebras.Affine(QQ, 'A1') sage: R._repr_generator(R.0) 'B[alpha[1]]' - sage: R = AffineLieConformalAlgebra(QQ, 'A1', names=('e','h','f')) + sage: R = lie_conformal_algebras.Affine(QQ, 'A1', names=('e','h','f')) sage: R._repr_generator(R.0) 'e' """ diff --git a/src/sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py new file mode 100644 index 00000000000..d581558aafa --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py @@ -0,0 +1,81 @@ +r""" +Neveu-Schwarz Super Lie Conformal Algebra + +The `N=1` or *Neveu-Schwarz* super Lie conformal algebra is a super +extension of the Virasoro Lie conformal algebra with generators `L` +and `C` by an odd primary generator `G` of conformal weight `3/2`. The +remaining `\lambda`-bracket is given by: + +.. MATH:: + + [G_\lambda G] = 2L + \frac{\lambda^2}{3} C. + +AUTHORS: + +- Reimundo Heluani (2020-06-03): Initial implementation. +""" +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra + +class NeveuSchwarzLieConformalAlgebra(GradedLieConformalAlgebra): + """ + The Neveu-Schwarz super Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative Ring; the base ring of this Lie + conformal algebra. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.NeveuSchwarz(AA); R + The Neveu-Schwarz super Lie conformal algebra over Algebraic Real Field + sage: R.structure_coefficients() + Finite family {('G', 'G'): ((0, 2*L), (2, 2/3*C)), ('G', 'L'): ((0, 1/2*TG), (1, 3/2*G)), ('L', 'G'): ((0, TG), (1, 3/2*G)), ('L', 'L'): ((0, TL), (1, 2*L), (3, 1/2*C))} + sage: R.inject_variables() + Defining L, G, C + sage: G.nproduct(G,0) + 2*L + sage: G.degree() + 3/2 + """ + def __init__(self, R): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: TestSuite(V).run() + """ + nsdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, + 3:{('C', 0):R(2).inverse_of_unit()}}, + ('L','G'):{0:{('G',1):1}, 1:{('G',0):R(3)*R(2).\ + inverse_of_unit()}}, ('G','G'): {0:{('L',0):2}, + 2:{('C',0):R(2)*R(3).inverse_of_unit()}}} + from sage.rings.rational_field import QQ + weights = (2,QQ(3/2)) + parity = (0,1) + GradedLieConformalAlgebra.__init__(self, R, nsdict, names=('L','G'), + central_elements=('C',), weights=weights, parity=parity) + + def _repr_(self): + """ + The name of this Lie Conformal algebra. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.NeveuSchwarz(GF(5)); R + The Neveu-Schwarz super Lie conformal algebra over Finite Field of size 5 + """ + return "The Neveu-Schwarz super Lie conformal algebra over {}".\ + format(self.base_ring()) diff --git a/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py new file mode 100644 index 00000000000..6d1c9d29b48 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py @@ -0,0 +1,79 @@ +r""" +Virasoro Lie Conformal Algebra + +The Virasoro Lie conformal algebra is generated by `L` and a central +element `C`. The `\lambda`-brackets are given by: + +.. MATH:: + + [L_\lambda L] = T L + 2 \lambda L + \frac{\lambda^3}{12} C. + +It is an H-graded Lie conformal algebra with `L` of degree `2`. + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra +class VirasoroLieConformalAlgebra(GradedLieConformalAlgebra): + """ + The Virasoro Lie Conformal algebra over `R`. + + INPUT: + + - ``R`` -- a commutative ring; behaviour is undefined if `R` is + not a Field of characteristic zero. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.category() + Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + sage: Vir.inject_variables() + Defining L, C + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + + TESTS:: + + sage: Vir.gens() + (L, C) + """ + def __init__(self, R): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: TestSuite(V).run() + """ + virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, + 3:{('C', 0):R(2).inverse_of_unit()}}} + GradedLieConformalAlgebra.__init__(self,R, virdict, + names = ('L',), central_elements = ('C',), weights = (2,)) + + def _repr_(self): + """ + The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: lie_conformal_algebras.Virasoro(QQbar) + The Virasoro Lie conformal algebra over Algebraic Field + """ + return "The Virasoro Lie conformal algebra over {}".format( + self.base_ring()) + + diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index 9023ce3948a..d2a39124712 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -139,5 +139,5 @@ # polyhedra lazy_import('sage.categories.polyhedra', 'PolyhedralSets') -# vertex algebras +# lie conformal algebras lazy_import('sage.categories.lie_conformal_algebras', 'LieConformalAlgebras') diff --git a/src/sage/categories/graded_lie_conformal_algebras.py b/src/sage/categories/graded_lie_conformal_algebras.py new file mode 100644 index 00000000000..6c38eb231fd --- /dev/null +++ b/src/sage/categories/graded_lie_conformal_algebras.py @@ -0,0 +1,158 @@ +r""" +Graded Lie Conformal Algebras + +A Lie conformal algebra `L` is called *H-Graded* [DSK2006]_ if there exists +a decomposition `L = \oplus L_n` such that the +`\lambda`-bracket becomes graded of degree `-1`, that is: + +.. MATH:: + + a_{(n)} b \in L_{p + q -n -1} \qquad + a \in L_p, \: b \in L_q, \: n \geq 0. + +In particular this implies that the action of `T` increases +degree by `1`. + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.graded_modules import GradedModulesCategory +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.super_modules import SuperModulesCategory +from sage.misc.abstract_method import abstract_method + +class GradedLieConformalAlgebras(GradedModulesCategory): + """ + The subcategory of H-graded Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field + """ + + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Super().Graded() + Category of super H-graded Lie conformal algebras over Algebraic Field + sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() + Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) + + + class SubcategoryMethods: + + def Super(self, base_ring=None): + """ + The subcategory of H-graded super Lie conformal algebras + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).Super().Graded() + Category of super H-graded Lie conformal algebras over Rational Field + """ + assert base_ring is None or base_ring is self.base_ring() + if isinstance(self,CategoryWithAxiom_over_base_ring): + axioms_whitelist = frozenset(["WithBasis", + "FinitelyGeneratedAsLieConformalAlgebra"]) + axioms = axioms_whitelist.intersection(self.axioms()) + return self.base_category().Super()._with_axioms(axioms) + return SuperModulesCategory.category_of(self) + + class WithBasis(CategoryWithAxiom_over_base_ring): + """ + The subcategory of H-graded Lie conformal algebras with + basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Graded().WithBasis() + Category of H-graded Lie conformal algebras with basis over Integer Ring + """ + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated H-graded + Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() + Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring + """ + pass + + class FinitelyGeneratedAsLieConformalAlgebra( + CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated H-graded Lie + conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(ZZ).Graded().FinitelyGenerated(); C + Category of finitely generated H-graded Lie conformal algebras over Integer Ring + sage: C is LieConformalAlgebras(ZZ).FinitelyGenerated().Graded() + True + """ + pass + + class Super(SuperModulesCategory): + """ + The subcategory of H-graded super Lie conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar).Graded().Super(); C + Category of super H-graded Lie conformal algebras over Algebraic Field + sage: C is LieConformalAlgebras(QQbar).Super().Graded() + True + """ + def extra_super_categories(self): + """ + The extra super categories of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super().super_categories() + [Category of finitely generated super Lie conformal algebras over Rational Field, + Category of finitely generated H-graded Lie conformal algebras over Rational Field, + Category of super H-graded Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + + class ElementMethods: + @abstract_method + def degree(self): + """ + The degree of this element. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.degree() + 2 + sage: L.T(3).degree() + 5 + """ + raise NotImplementedError("Not implemented") diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index 34857b4fee9..6c3e47e0cee 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -130,8 +130,7 @@ from sage.categories.modules import Modules from .category_types import Category_over_base_ring -from sage.categories.category_with_axiom import \ - CategoryWithAxiom_over_base_ring +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.structure.element import coerce_binop @@ -261,8 +260,8 @@ def example(self): sage: LieConformalAlgebras(QQ).example() The Virasoro Lie conformal algebra over Rational Field """ - from sage.algebras.lie_conformal_algebras.examples import \ - VirasoroLieConformalAlgebra + from sage.algebras.lie_conformal_algebras.virasoro_lie_conformal_algebra\ + import VirasoroLieConformalAlgebra return VirasoroLieConformalAlgebra(self.base_ring()) def _repr_object_names(self): @@ -280,7 +279,7 @@ def _repr_object_names(self): class ParentMethods: - @abstract_method + @abstract_method(optional=True) def ideal(self, *gens, **kwds): """ The ideal of this conformal algebra generated by ``gens``. @@ -290,7 +289,7 @@ def ideal(self, *gens, **kwds): Ideals of Lie Conformal Algebras are not implemented yet. """ - raise NotImplementedError("Ideals of Lie Conformal algebras are "\ + raise NotImplementedError("ideals of Lie Conformal algebras are "\ "not implemented yet") def is_super(self): @@ -300,10 +299,10 @@ def is_super(self): EXAMPLES:: - sage: V = VirasoroLieConformalAlgebra(QQ) + sage: V = lie_conformal_algebras.Virasoro(QQ) sage: V.is_super() False - sage: NeveuSchwarzLieConformalAlgebra(QQbar).is_super() + sage: lie_conformal_algebras.NeveuSchwarz(QQbar).is_super() True Notice that we can force to have a *purely even* super Lie @@ -324,7 +323,7 @@ def is_graded(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir The Virasoro Lie conformal algebra over Rational Field sage: Vir.is_graded() @@ -342,7 +341,7 @@ def bracket(self,rhs): The brackets of the Virasoro Lie conformal Algebra:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L.bracket(L) {0: TL, 1: 2*L, 3: 1/2*C} sage: L.bracket(L.T()) @@ -350,7 +349,7 @@ def bracket(self,rhs): Now with a current algebra:: - sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: V.gens() (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) sage: E = V.0; H = V.1; F = V.2; @@ -359,13 +358,13 @@ def bracket(self,rhs): sage: E.bracket(F) {0: B[alphacheck[1]], 1: B['K']} - With a super Lie Conformal Algebra:: + With a super Lie conformal algebra:: - sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); + sage: R = lie_conformal_algebras.NeveuSchwarz(QQbar) sage: R.inject_variables() Defining L, G, C - sage: G.bracket(G) - {0: 2*L, 2: 2/3*C} + sage: G.bracket(G.T()) + {0: 2*TL, 1: 2*L, 3: 2*C} .. NOTE:: @@ -384,7 +383,7 @@ def _bracket_(self,rhs): The brackets of the Virasoro Lie conformal Algebra:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L._bracket_(L) {0: TL, 1: 2*L, 3: 1/2*C} sage: L._bracket_(L.T()) @@ -392,7 +391,7 @@ def _bracket_(self,rhs): Now with a current algebra:: - sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: V.gens() (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) sage: E = V.0; H = V.1; F = V.2; @@ -415,12 +414,12 @@ def nproduct(self,rhs,n): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L.nproduct(L,3) 1/2*C sage: L.nproduct(L.T(),0) 2*T^(2)L - sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: E = V.0; H = V.1; F = V.2; sage: E.nproduct(H,0) == - 2*E True @@ -439,14 +438,17 @@ def _nproduct_(self,rhs,n): r""" The ``n``-th product of these two elements. + If `n\geq 0` it returns the element of this Lie conformal + algebra. + EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L._nproduct_(L,3) 1/2*C sage: L._nproduct_(L.T(),0) 2*T^(2)L - sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: E = V.0; H = V.1; F = V.2; sage: E._nproduct_(H,0) == - 2*E True @@ -461,9 +463,9 @@ def _nproduct_(self,rhs,n): if n >= 0: return self.bracket(rhs).get(n,self.parent().zero()) else: - raise ValueError("Vertex algebras are not implemented yet") + raise NotImplementedError("vertex algebras are not implemented yet") - @abstract_method(optional=False) + @abstract_method def T(self, n=1): r""" The n-th derivative of this element. @@ -480,7 +482,7 @@ def T(self, n=1): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.inject_variables() Defining L, C sage: L.T() @@ -490,7 +492,6 @@ def T(self, n=1): sage: C.T() 0 """ - raise NotImplementedError("Not implemented") @abstract_method def is_even_odd(self): @@ -500,7 +501,7 @@ def is_even_odd(self): EXAMPLES:: - sage: R = NeveuSchwarzLieConformalAlgebra(QQ); + sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); sage: R.inject_variables() Defining L, G, C sage: G.is_even_odd() @@ -534,18 +535,6 @@ def FinitelyGenerated(self): """ return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") - def WithBasis(self): - """ - The subcategory of Lie conformal algebras with a preferred - basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).WithBasis() - Category of Lie conformal algebras over Rational Field with basis - """ - return self._with_axiom("WithBasis") - def Graded(self, base_ring=None): """ The subcategory of H-Graded Lie conformal algebras. @@ -615,8 +604,8 @@ def example(self): sage: LieConformalAlgebras(QQ).Super().example() The Neveu-Schwarz super Lie conformal algebra over Rational Field """ - from sage.algebras.lie_conformal_algebras.examples import \ - NeveuSchwarzLieConformalAlgebra + from sage.algebras.lie_conformal_algebras.neveu_schwarz_lie_conformal_algebra\ + import NeveuSchwarzLieConformalAlgebra return NeveuSchwarzLieConformalAlgebra(self.base_ring()) class SubcategoryMethods: @@ -680,137 +669,8 @@ class FinitelyGeneratedAsLieConformalAlgebra( """ pass - - class Graded(GradedModulesCategory): - """ - The subcategory of H-graded Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Graded() - Category of H-graded Lie conformal algebras over Algebraic Field - """ - - def _repr_object_names(self): - """ - The names of the objects of this category - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Super().Graded() - Category of super H-graded Lie conformal algebras over Algebraic Field - sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() - Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring - """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) - - - class SubcategoryMethods: - - def Super(self, base_ring=None): - """ - The subcategory of H-graded super Lie conformal algebras - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).Super().Graded() - Category of super H-graded Lie conformal algebras over Rational Field - """ - assert base_ring is None or base_ring is self.base_ring() - if isinstance(self,CategoryWithAxiom_over_base_ring): - axioms_whitelist = frozenset(["WithBasis", - "FinitelyGeneratedAsLieConformalAlgebra"]) - axioms = axioms_whitelist.intersection(self.axioms()) - return self.base_category().Super()._with_axioms(axioms) - return SuperModulesCategory.category_of(self) - - class WithBasis(CategoryWithAxiom_over_base_ring): - """ - The subcategory of H-graded Lie conformal algebras with - basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(ZZ).Graded().WithBasis() - Category of H-graded Lie conformal algebras with basis over Integer Ring - """ - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated H-graded - Lie conformal algebras with basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() - Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring - """ - pass - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated H-graded Lie - conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(ZZ).Graded().FinitelyGenerated(); C - Category of finitely generated H-graded Lie conformal algebras over Integer Ring - sage: C is LieConformalAlgebras(ZZ).FinitelyGenerated().Graded() - True - """ - pass - - class Super(SuperModulesCategory): - """ - The subcategory of H-graded super Lie conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(QQbar).Graded().Super(); C - Category of super H-graded Lie conformal algebras over Algebraic Field - sage: C is LieConformalAlgebras(QQbar).Super().Graded() - True - """ - def extra_super_categories(self): - """ - The extra super categories of this category. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super().super_categories() - [Category of super H-graded Lie conformal algebras over Rational Field, - Category of finitely generated super Lie conformal algebras over Rational Field, - Category of finitely generated H-graded Lie conformal algebras over Rational Field] - """ - return [self.base_category(),] - - - class ElementMethods: - @abstract_method - def degree(self): - """ - The degree of this element. - - EXAMPLES:: - - sage: Vir = VirasoroLieConformalAlgebra(QQ); L = Vir.0 - sage: L.degree() - 2 - sage: L.T(3).degree() - 5 - - sage: R = NeveuSchwarzLieConformalAlgebra(QQbar); - sage: R.inject_variables() - Defining L, G, C - sage: G.degree() - 3/2 - """ - raise NotImplementedError("Not implemented") - + from .graded_lie_conformal_algebras import GradedLieConformalAlgebras + Graded = GradedLieConformalAlgebras class WithBasis(CategoryWithAxiom_over_base_ring): """ The subcategory of Lie conformal algebras with basis. @@ -840,7 +700,7 @@ def index(self): EXAMPLES:: - sage: V = NeveuSchwarzLieConformalAlgebra(QQ) + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) sage: V.inject_variables() Defining L, G, C sage: G.T(3).index() @@ -917,19 +777,13 @@ def ngens(self): EXAMPLES:: - sage: Vir = VirasoroLieConformalAlgebra(QQ) + sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.ngens() 2 - sage: V = AffineLieConformalAlgebra(QQ, 'A2') + sage: V = lie_conformal_algebras.Affine(QQ, 'A2') sage: V.ngens() 9 - - sage: L = NeveuSchwarzLieConformalAlgebra(QQbar); - sage: L.gens() - (L, G, C) - sage: L.ngens() - 3 """ return len(self.gens()) @@ -939,7 +793,7 @@ def gen(self,i): EXAMPLES:: - sage: V = AffineLieConformalAlgebra(QQ, 'A1') + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: V.gens() (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) sage: V.gen(0) From 0a154deeda7e1dcd64420d6f621078845808ba84 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Thu, 2 Jul 2020 07:29:28 -0300 Subject: [PATCH 038/379] Added missing docstrings --- .../lie_conformal_algebra_with_structure_coefs.py | 7 +++++++ src/sage/categories/lie_conformal_algebras.py | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py index f8b0028e335..ac934107742 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -140,6 +140,13 @@ def _standardize_s_coeff(s_coeff, index_set, ce, parity=None): It contains superfluous information that can be obtained by skew-symmetry but that improves speed in computing OPE for vertex algebras. + + EXAMPLES:: + + sage: virdict = {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2},3:{('C', 0):1/2}}} + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir._standardize_s_coeff(virdict, Family(('L',)), ('C',)) + Finite family {('L', 'L'): ((0, ((('L', 1), 1),)), (1, ((('L', 0), 2),)), (3, ((('C', 0), 1/2),)))} """ if parity is None: parity = (0,)*index_set.cardinality() diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index 6c3e47e0cee..f70dab63b01 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -279,7 +279,6 @@ def _repr_object_names(self): class ParentMethods: - @abstract_method(optional=True) def ideal(self, *gens, **kwds): """ The ideal of this conformal algebra generated by ``gens``. @@ -288,6 +287,14 @@ def ideal(self, *gens, **kwds): Ideals of Lie Conformal Algebras are not implemented yet. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ideal() + Traceback (most recent call last): + ... + NotImplementedError: ideals of Lie Conformal algebras are not implemented yet """ raise NotImplementedError("ideals of Lie Conformal algebras are "\ "not implemented yet") From 393200b1bd58cb03a4720a85dcb09d6249deac33 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 2 Jul 2020 12:52:15 -0700 Subject: [PATCH 039/379] sage.numerical.backends.matrix_sdp_backend: Add stub module --- .../numerical/backends/matrix_sdp_backend.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/sage/numerical/backends/matrix_sdp_backend.py diff --git a/src/sage/numerical/backends/matrix_sdp_backend.py b/src/sage/numerical/backends/matrix_sdp_backend.py new file mode 100644 index 00000000000..3ee06c0cad5 --- /dev/null +++ b/src/sage/numerical/backends/matrix_sdp_backend.py @@ -0,0 +1,24 @@ +r""" +Matrix Backend for SDP solvers + +It stores the SDP data in Sage matrices. It allow users to specify a base ring +and can store exact SDPs with rational or algebraic data. + +The class does not provide a solver. It can be used as a base class for +user-defined classes implementing solvers. + +""" + +#***************************************************************************** +# Copyright (C) 2020 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +class MatrixSDPBackend: + + pass From 978b9eb87b22839f5856db1d923f2bc89d70447c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 2 Jul 2020 16:05:03 -0700 Subject: [PATCH 040/379] sage.numerical.backends.matrix_sdp_backend.MatrixSDPBackend: Add __init__, set_sense, is_maximization --- .../numerical/backends/matrix_sdp_backend.py | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/backends/matrix_sdp_backend.py b/src/sage/numerical/backends/matrix_sdp_backend.py index 3ee06c0cad5..524567df6f2 100644 --- a/src/sage/numerical/backends/matrix_sdp_backend.py +++ b/src/sage/numerical/backends/matrix_sdp_backend.py @@ -19,6 +19,58 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -class MatrixSDPBackend: +from .generic_sdp_backend import GenericSDPBackend - pass +class MatrixSDPBackend(GenericSDPBackend): + + def __init__(self, base_ring=None): + + super(MatrixSDPBackend, self).__init__() + + if base_ring is None: + from sage.rings.all import QQ + base_ring = QQ + self._base_ring = base_ring + + self.set_sense(+1) + + def set_sense(self, sense): + """ + Set the direction (maximization/minimization). + + INPUT: + + - ``sense`` (integer) : + + * +1 => Maximization + * -1 => Minimization + + EXAMPLES:: + + sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver=MatrixSDPBackend) + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + self._sense = sense + + def is_maximization(self): + """ + Test whether the problem is a maximization + + EXAMPLES:: + + sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver=MatrixSDPBackend) + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + return self._sense == +1 From 72fad876baacda7c9a7adddeb11cc20ec5f22cd5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 2 Jul 2020 16:10:20 -0700 Subject: [PATCH 041/379] sage.numerical.backends.matrix_sdp_backend.MatrixSDPBackend: Add problem_name --- .../backends/generic_sdp_backend.pyx | 2 +- .../numerical/backends/matrix_sdp_backend.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/sage/numerical/backends/generic_sdp_backend.pyx b/src/sage/numerical/backends/generic_sdp_backend.pyx index 664eaf14877..746fd27f6af 100644 --- a/src/sage/numerical/backends/generic_sdp_backend.pyx +++ b/src/sage/numerical/backends/generic_sdp_backend.pyx @@ -403,7 +403,7 @@ cdef class GenericSDPBackend: sage: from sage.numerical.backends.generic_sdp_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.problem_name("There once was a french fry") # optional - Nonexistent_LP_solver - sage: print(p.get_problem_name()) # optional - Nonexistent_LP_solver + sage: print(p.problem_name()) # optional - Nonexistent_LP_solver There once was a french fry """ diff --git a/src/sage/numerical/backends/matrix_sdp_backend.py b/src/sage/numerical/backends/matrix_sdp_backend.py index 524567df6f2..ece4f3d4388 100644 --- a/src/sage/numerical/backends/matrix_sdp_backend.py +++ b/src/sage/numerical/backends/matrix_sdp_backend.py @@ -33,6 +33,7 @@ def __init__(self, base_ring=None): self._base_ring = base_ring self.set_sense(+1) + self.problem_name("") def set_sense(self, sense): """ @@ -74,3 +75,26 @@ def is_maximization(self): False """ return self._sense == +1 + + def problem_name(self, name=None): + """ + Return or define the problem's name + + INPUT: + + - ``name`` (``str``) -- the problem's name. When set to + ``None`` (default), the method returns the problem's name. + + EXAMPLES:: + + sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver=MatrixSDPBackend) + sage: p.problem_name("There once was a french fry") + sage: print(p.problem_name()) + There once was a french fry + """ + if name is None: + return self._prob_name + else: + self._prob_name = name From 80dae7d895b379b13bdc435f226b8de6b034b5be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 2 Jul 2020 17:20:15 -0700 Subject: [PATCH 042/379] Refactor CVXOPTSDPBackend through MatrixSDPBackend --- src/module_list.py | 3 + .../numerical/backends/cvxopt_sdp_backend.pyx | 446 +--------------- .../numerical/backends/matrix_sdp_backend.pxd | 13 + .../numerical/backends/matrix_sdp_backend.py | 100 ---- .../numerical/backends/matrix_sdp_backend.pyx | 485 ++++++++++++++++++ 5 files changed, 506 insertions(+), 541 deletions(-) create mode 100644 src/sage/numerical/backends/matrix_sdp_backend.pxd delete mode 100644 src/sage/numerical/backends/matrix_sdp_backend.py create mode 100644 src/sage/numerical/backends/matrix_sdp_backend.pyx diff --git a/src/module_list.py b/src/module_list.py index a5ee17623cf..65338cb6740 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -783,6 +783,9 @@ Extension("sage.numerical.backends.generic_sdp_backend", ["sage/numerical/backends/generic_sdp_backend.pyx"]), + Extension("sage.numerical.backends.matrix_sdp_backend", + ["sage/numerical/backends/matrix_sdp_backend.pyx"]), + Extension("sage.numerical.backends.glpk_backend", ["sage/numerical/backends/glpk_backend.pyx"]), diff --git a/src/sage/numerical/backends/cvxopt_sdp_backend.pyx b/src/sage/numerical/backends/cvxopt_sdp_backend.pyx index 6eb9eebe14b..f783ae51f9e 100644 --- a/src/sage/numerical/backends/cvxopt_sdp_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_sdp_backend.pyx @@ -21,21 +21,15 @@ AUTHORS: from sage.numerical.sdp import SDPSolverException from sage.matrix.all import Matrix -from .generic_sdp_backend cimport GenericSDPBackend +from .matrix_sdp_backend cimport MatrixSDPBackend -cdef class CVXOPTSDPBackend(GenericSDPBackend): - cdef list objective_function #c_matrix - cdef list coeffs_matrix - cdef bint is_maximize +cdef class CVXOPTSDPBackend(MatrixSDPBackend): - cdef list row_name_var - cdef list col_name_var cdef dict answer cdef dict param - cdef str name - def __cinit__(self, maximization = True): + def __init__(self, maximization=True): """ Cython constructor @@ -46,15 +40,8 @@ cdef class CVXOPTSDPBackend(GenericSDPBackend): """ - self.objective_function = [] #c_matrix in the example for cvxopt - self.name = "" - self.coeffs_matrix = [] - self.obj_constant_term = 0 - self.matrices_dim = {} - self.is_maximize = 1 - - self.row_name_var = [] - self.col_name_var = [] + from sage.rings.all import RDF + MatrixSDPBackend.__init__(self, maximization, RDF) self.param = {"show_progress":False, "maxiters":100, @@ -63,263 +50,6 @@ cdef class CVXOPTSDPBackend(GenericSDPBackend): "feastol":1e-7, "refinement":1 } self.answer = {} - if maximization: - self.set_sense(+1) - else: - self.set_sense(-1) - - def get_matrix(self): - """ - Get a block of a matrix coefficient - - EXAMPLES:: - - sage: p = SemidefiniteProgram(solver="cvxopt") - sage: x = p.new_variable() - sage: a1 = matrix([[1, 2.], [2., 3.]]) - sage: a2 = matrix([[3, 4.], [4., 5.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] <= a1) - sage: b = p.get_backend() - sage: b.get_matrix()[0][0] - ( - [-1.0 -2.0] - -1, [-2.0 -3.0] - ) - - """ - return self.coeffs_matrix - - cpdef int add_variable(self, obj=0.0, name=None) except -1: - """ - Add a variable. - - This amounts to adding a new column of matrices to the matrix. By default, - the variable is both positive and real. - - INPUT: - - - ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0) - - - ``name`` - an optional name for the newly added variable (default: ``None``). - - OUTPUT: The index of the newly created variable - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() - 0 - sage: p.add_variable() - 0 - sage: p.ncols() - 1 - sage: p.add_variable() - 1 - sage: p.add_variable(name='x',obj=1.0) - 2 - sage: p.col_name(2) - 'x' - sage: p.objective_coefficient(2) - 1.00000000000000 - """ - i = 0 - for row in self.coeffs_matrix: - if self.matrices_dim.get(i) is not None: - row.append( Matrix.zero(self.matrices_dim[i], self.matrices_dim[i]) ) - else: - row.append(0) - i+=1 - self.col_name_var.append(name) - self.objective_function.append(obj) - return len(self.objective_function) - 1 - - - cpdef int add_variables(self, int n, names=None) except -1: - """ - Add ``n`` variables. - - This amounts to adding new columns to the matrix. By default, - the variables are both positive and real. - - INPUT: - - - ``n`` - the number of new variables (must be > 0) - - - ``names`` - optional list of names (default: ``None``) - - OUTPUT: The index of the variable created last. - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() - 0 - sage: p.add_variables(5) - 4 - sage: p.ncols() - 5 - sage: p.add_variables(2, names=['a','b']) - 6 - """ - for i in range(n): - self.add_variable() - return len(self.objective_function) - 1; - - - cpdef set_sense(self, int sense): - """ - Set the direction (maximization/minimization). - - INPUT: - - - ``sense`` (integer) : - - * +1 => Maximization - * -1 => Minimization - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.is_maximization() - True - sage: p.set_sense(-1) - sage: p.is_maximization() - False - """ - if sense == 1: - self.is_maximize = 1 - else: - self.is_maximize = 0 - - cpdef objective_coefficient(self, int variable, coeff=None): - """ - Set or get the coefficient of a variable in the objective - function - - INPUT: - - - ``variable`` (integer) -- the variable's id - - - ``coeff`` (double) -- its coefficient - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable() - 0 - sage: p.objective_coefficient(0) - 0.0 - sage: p.objective_coefficient(0,2) - sage: p.objective_coefficient(0) - 2.0 - """ - if coeff is not None: - self.objective_function[variable] = float(coeff); - else: - return self.objective_function[variable] - - cpdef set_objective(self, list coeff, d=0.0): - """ - Set the objective function. - - INPUT: - - - ``coeff`` -- a list of real values, whose ith element is the - coefficient of the ith variable in the objective function. - - - ``d`` (double) -- the constant term in the linear function (set to `0` by default) - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) - 4 - sage: p.set_objective([1, 1, 2, 1, 3]) - sage: [p.objective_coefficient(x) for x in range(5)] - [1, 1, 2, 1, 3] - """ - for i in range(len(coeff)): - self.objective_function[i] = coeff[i]; - obj_constant_term = d; - - - - cpdef add_linear_constraint(self, coefficients, name=None): - """ - Add a linear constraint. - - INPUT: - - - ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` - is a variable index (integer) and ``v`` is a value (matrix). - The pairs come sorted by indices. If c is -1 it - represents the constant coefficient. - - - ``name`` - an optional name for this row (default: ``None``) - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(2) - 1 - sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )]) - sage: p.row(0) - ([0, 1], - [ - [ 33.0000000000000 -9.00000000000000] - [-9.00000000000000 26.0000000000000], - - [-7.00000000000000 -11.0000000000000] - [-11.0000000000000 3.00000000000000] - ]) - sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )],name="fun") - sage: p.row_name(-1) - 'fun' - """ - coefficients = list(coefficients) - from sage.structure.element import is_Matrix - for t in coefficients: - m = t[1] - if not is_Matrix(m): - raise ValueError("The coefficients must be matrices") - if not m.is_square(): - raise ValueError("The matrix has to be a square") - if self.matrices_dim.get(self.nrows()) is not None and m.dimensions()[0] != self.matrices_dim.get(self.nrows()): - raise ValueError("the matrices have to be of the same dimension") - self.coeffs_matrix.append(coefficients) - self.matrices_dim[self.nrows()] = m.dimensions()[0] # - self.row_name_var.append(name) - - cpdef add_linear_constraints(self, int number, names=None): - """ - Add constraints. - - INPUT: - - - ``number`` (integer) -- the number of constraints to add. - - - ``names`` - an optional list of names (default: ``None``) - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) - 4 - sage: p.add_linear_constraints(5) - sage: p.row(4) - ([], []) - """ - for i in range(number): - self.add_linear_constraint(zip(range(self.ncols()+1),[Matrix.zero(1,1) for i in range(self.ncols()+1)]), - name=None if names is None else names[i]) - cpdef int solve(self) except -1: @@ -524,125 +254,6 @@ cdef class CVXOPTSDPBackend(GenericSDPBackend): """ return self.answer['x'][variable] - cpdef int ncols(self): - """ - Return the number of columns/variables. - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() - 0 - sage: p.add_variables(2) - 1 - sage: p.ncols() - 2 - """ - - return len(self.objective_function) - - cpdef int nrows(self): - """ - Return the number of rows/constraints. - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.nrows() - 0 - sage: p.add_variables(5) - 4 - sage: p.add_linear_constraints(2) - sage: p.nrows() - 2 - """ - return len(self.matrices_dim) - - - cpdef bint is_maximization(self): - """ - Test whether the problem is a maximization - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.is_maximization() - True - sage: p.set_sense(-1) - sage: p.is_maximization() - False - """ - if self.is_maximize == 1: - return 1 - else: - return 0 - - cpdef problem_name(self, name=None): - """ - Return or define the problem's name - - INPUT: - - - ``name`` (``str``) -- the problem's name. When set to - ``NULL`` (default), the method returns the problem's name. - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.problem_name("There once was a french fry") - sage: print(p.problem_name()) - There once was a french fry - """ - if name is None: - return self.name - - self.name = name - - - cpdef row(self, int i): - """ - Return a row - - INPUT: - - - ``index`` (integer) -- the constraint's id. - - OUTPUT: - - A pair ``(indices, coeffs)`` where ``indices`` lists the - entries whose coefficient is nonzero, and to which ``coeffs`` - associates their coefficient on the model of the - ``add_linear_constraint`` method. - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) - 4 - sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )]) - sage: p.row(0) - ([0, 1], - [ - [ 33.0000000000000 -9.00000000000000] - [-9.00000000000000 26.0000000000000], - - [-7.00000000000000 -11.0000000000000] - [-11.0000000000000 3.00000000000000] - ]) - """ - indices = [] - matrices = [] - for index,m in self.coeffs_matrix[i]: - if m != Matrix.zero(self.matrices_dim[i],self.matrices_dim[i]): - indices.append(index) - matrices.append(m) - return (indices, matrices) - cpdef dual_variable(self, int i, sparse=False): """ The `i`-th dual variable @@ -744,52 +355,6 @@ cdef class CVXOPTSDPBackend(GenericSDPBackend): assert(n == self.answer['ss'][i].size[1]) # must be square matrix return Matrix(n, n, list(self.answer['ss'][i]), sparse=sparse) - cpdef row_name(self, int index): - """ - Return the ``index`` th row name - - INPUT: - - - ``index`` (integer) -- the row's id - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_linear_constraints(1, names="A") - sage: p.row_name(0) - 'A' - - """ - if self.row_name_var[index] is not None: - return self.row_name_var[index] - return "constraint_" + repr(index) - - cpdef col_name(self, int index): - """ - Return the ``index`` th col name - - INPUT: - - - ``index`` (integer) -- the col's id - - - ``name`` (``char *``) -- its name. When set to ``NULL`` - (default), the method returns the current name. - - EXAMPLES:: - - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable(name="I am a variable") - 0 - sage: p.col_name(0) - 'I am a variable' - """ - if self.col_name_var[index] is not None: - return self.col_name_var[index] - return "x_" + repr(index) - - cpdef solver_parameter(self, name, value = None): """ @@ -821,4 +386,3 @@ cdef class CVXOPTSDPBackend(GenericSDPBackend): return self.param[name] else: self.param[name] = value - diff --git a/src/sage/numerical/backends/matrix_sdp_backend.pxd b/src/sage/numerical/backends/matrix_sdp_backend.pxd new file mode 100644 index 00000000000..4ebbf01a16d --- /dev/null +++ b/src/sage/numerical/backends/matrix_sdp_backend.pxd @@ -0,0 +1,13 @@ +from .generic_sdp_backend cimport GenericSDPBackend + +cdef class MatrixSDPBackend(GenericSDPBackend): + + cdef list objective_function + cdef list coeffs_matrix + cdef bint is_maximize + + cdef list row_name_var + cdef list col_name_var + cdef str name + + cdef object _base_ring diff --git a/src/sage/numerical/backends/matrix_sdp_backend.py b/src/sage/numerical/backends/matrix_sdp_backend.py deleted file mode 100644 index ece4f3d4388..00000000000 --- a/src/sage/numerical/backends/matrix_sdp_backend.py +++ /dev/null @@ -1,100 +0,0 @@ -r""" -Matrix Backend for SDP solvers - -It stores the SDP data in Sage matrices. It allow users to specify a base ring -and can store exact SDPs with rational or algebraic data. - -The class does not provide a solver. It can be used as a base class for -user-defined classes implementing solvers. - -""" - -#***************************************************************************** -# Copyright (C) 2020 Matthias Koeppe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from .generic_sdp_backend import GenericSDPBackend - -class MatrixSDPBackend(GenericSDPBackend): - - def __init__(self, base_ring=None): - - super(MatrixSDPBackend, self).__init__() - - if base_ring is None: - from sage.rings.all import QQ - base_ring = QQ - self._base_ring = base_ring - - self.set_sense(+1) - self.problem_name("") - - def set_sense(self, sense): - """ - Set the direction (maximization/minimization). - - INPUT: - - - ``sense`` (integer) : - - * +1 => Maximization - * -1 => Minimization - - EXAMPLES:: - - sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver=MatrixSDPBackend) - sage: p.is_maximization() - True - sage: p.set_sense(-1) - sage: p.is_maximization() - False - """ - self._sense = sense - - def is_maximization(self): - """ - Test whether the problem is a maximization - - EXAMPLES:: - - sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver=MatrixSDPBackend) - sage: p.is_maximization() - True - sage: p.set_sense(-1) - sage: p.is_maximization() - False - """ - return self._sense == +1 - - def problem_name(self, name=None): - """ - Return or define the problem's name - - INPUT: - - - ``name`` (``str``) -- the problem's name. When set to - ``None`` (default), the method returns the problem's name. - - EXAMPLES:: - - sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver=MatrixSDPBackend) - sage: p.problem_name("There once was a french fry") - sage: print(p.problem_name()) - There once was a french fry - """ - if name is None: - return self._prob_name - else: - self._prob_name = name diff --git a/src/sage/numerical/backends/matrix_sdp_backend.pyx b/src/sage/numerical/backends/matrix_sdp_backend.pyx new file mode 100644 index 00000000000..b995ae41400 --- /dev/null +++ b/src/sage/numerical/backends/matrix_sdp_backend.pyx @@ -0,0 +1,485 @@ +r""" +Matrix Backend for SDP solvers + +It stores the SDP data in Sage matrices. It allows users to specify a base ring +and can store either floating-point SDPs or exact SDPs with rational or algebraic data. + +The class does not provide a solver method. It can be used as a base class for +other classes implementing solvers. + +""" + +#***************************************************************************** +# Copyright (C) 2014 Ingolfur Edvardsson +# Copyright (C) 2020 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.matrix.all import Matrix +from .generic_sdp_backend cimport GenericSDPBackend + +cdef class MatrixSDPBackend(GenericSDPBackend): + + def __init__(self, maximization=True, base_ring=None): + """ + Cython constructor + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + + """ + self.objective_function = [] + self.name = "" + self.coeffs_matrix = [] + self.obj_constant_term = 0 + self.matrices_dim = {} + self.is_maximize = 1 + + self.row_name_var = [] + self.col_name_var = [] + + if maximization: + self.set_sense(+1) + else: + self.set_sense(-1) + + if base_ring is None: + from sage.rings.all import QQ + base_ring = QQ + self._base_ring = base_ring + + cpdef base_ring(self): + """ + The base ring + + TESTS:: + + sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend + sage: MatrixSDPBackend(base_ring=QQ).base_ring() + Rational Field + """ + return self._base_ring + + def get_matrix(self): + """ + Get a block of a matrix coefficient + + EXAMPLES:: + + sage: p = SemidefiniteProgram(solver="cvxopt") + sage: x = p.new_variable() + sage: a1 = matrix([[1, 2.], [2., 3.]]) + sage: a2 = matrix([[3, 4.], [4., 5.]]) + sage: p.add_constraint(a1*x[0] + a2*x[1] <= a1) + sage: b = p.get_backend() + sage: b.get_matrix()[0][0] + ( + [-1.0 -2.0] + -1, [-2.0 -3.0] + ) + + """ + return self.coeffs_matrix + + cpdef int add_variable(self, obj=0.0, name=None) except -1: + """ + Add a variable. + + This amounts to adding a new column of matrices to the matrix. By default, + the variable is both positive and real. + + INPUT: + + - ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0) + + - ``name`` - an optional name for the newly added variable (default: ``None``). + + OUTPUT: The index of the newly created variable + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.ncols() + 1 + sage: p.add_variable() + 1 + sage: p.add_variable(name='x',obj=1.0) + 2 + sage: p.col_name(2) + 'x' + sage: p.objective_coefficient(2) + 1.00000000000000 + """ + i = 0 + for row in self.coeffs_matrix: + if self.matrices_dim.get(i) is not None: + row.append( Matrix.zero(self.matrices_dim[i], self.matrices_dim[i]) ) + else: + row.append(0) + i+=1 + self.col_name_var.append(name) + self.objective_function.append(obj) + return len(self.objective_function) - 1 + + + cpdef int add_variables(self, int n, names=None) except -1: + """ + Add ``n`` variables. + + This amounts to adding new columns to the matrix. By default, + the variables are both positive and real. + + INPUT: + + - ``n`` - the number of new variables (must be > 0) + + - ``names`` - optional list of names (default: ``None``) + + OUTPUT: The index of the variable created last. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.ncols() + 0 + sage: p.add_variables(5) + 4 + sage: p.ncols() + 5 + sage: p.add_variables(2, names=['a','b']) + 6 + """ + for i in range(n): + self.add_variable() + return len(self.objective_function) - 1; + + + cpdef set_sense(self, int sense): + """ + Set the direction (maximization/minimization). + + INPUT: + + - ``sense`` (integer) : + + * +1 => Maximization + * -1 => Minimization + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + if sense == 1: + self.is_maximize = 1 + else: + self.is_maximize = 0 + + cpdef objective_coefficient(self, int variable, coeff=None): + """ + Set or get the coefficient of a variable in the objective + function + + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``coeff`` (double) -- its coefficient + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_variable() + 0 + sage: p.objective_coefficient(0) + 0.0 + sage: p.objective_coefficient(0,2) + sage: p.objective_coefficient(0) + 2.0 + """ + if coeff is not None: + self.objective_function[variable] = float(coeff); + else: + return self.objective_function[variable] + + cpdef set_objective(self, list coeff, d=0.0): + """ + Set the objective function. + + INPUT: + + - ``coeff`` -- a list of real values, whose ith element is the + coefficient of the ith variable in the objective function. + + - ``d`` (double) -- the constant term in the linear function (set to `0` by default) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_variables(5) + 4 + sage: p.set_objective([1, 1, 2, 1, 3]) + sage: [p.objective_coefficient(x) for x in range(5)] + [1, 1, 2, 1, 3] + """ + for i in range(len(coeff)): + self.objective_function[i] = coeff[i]; + obj_constant_term = d; + + + + cpdef add_linear_constraint(self, coefficients, name=None): + """ + Add a linear constraint. + + INPUT: + + - ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` + is a variable index (integer) and ``v`` is a value (matrix). + The pairs come sorted by indices. If c is -1 it + represents the constant coefficient. + + - ``name`` - an optional name for this row (default: ``None``) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )]) + sage: p.row(0) + ([0, 1], + [ + [ 33.0000000000000 -9.00000000000000] + [-9.00000000000000 26.0000000000000], + + [-7.00000000000000 -11.0000000000000] + [-11.0000000000000 3.00000000000000] + ]) + sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )],name="fun") + sage: p.row_name(-1) + 'fun' + """ + coefficients = list(coefficients) + from sage.structure.element import is_Matrix + for t in coefficients: + m = t[1] + if not is_Matrix(m): + raise ValueError("The coefficients must be matrices") + if not m.is_square(): + raise ValueError("The matrix has to be a square") + if self.matrices_dim.get(self.nrows()) is not None and m.dimensions()[0] != self.matrices_dim.get(self.nrows()): + raise ValueError("the matrices have to be of the same dimension") + self.coeffs_matrix.append(coefficients) + self.matrices_dim[self.nrows()] = m.dimensions()[0] # + self.row_name_var.append(name) + + cpdef add_linear_constraints(self, int number, names=None): + """ + Add constraints. + + INPUT: + + - ``number`` (integer) -- the number of constraints to add. + + - ``names`` - an optional list of names (default: ``None``) + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraints(5) + sage: p.row(4) + ([], []) + """ + for i in range(number): + self.add_linear_constraint(zip(range(self.ncols()+1),[Matrix.zero(1,1) for i in range(self.ncols()+1)]), + name=None if names is None else names[i]) + + + cpdef int ncols(self): + """ + Return the number of columns/variables. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.ncols() + 0 + sage: p.add_variables(2) + 1 + sage: p.ncols() + 2 + """ + + return len(self.objective_function) + + cpdef int nrows(self): + """ + Return the number of rows/constraints. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.nrows() + 0 + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraints(2) + sage: p.nrows() + 2 + """ + return len(self.matrices_dim) + + + cpdef bint is_maximization(self): + """ + Test whether the problem is a maximization + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + if self.is_maximize == 1: + return 1 + else: + return 0 + + cpdef problem_name(self, name=None): + """ + Return or define the problem's name + + INPUT: + + - ``name`` (``str``) -- the problem's name. When set to + ``NULL`` (default), the method returns the problem's name. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.problem_name("There once was a french fry") + sage: print(p.problem_name()) + There once was a french fry + """ + if name is None: + return self.name + + self.name = name + + + cpdef row(self, int i): + """ + Return a row + + INPUT: + + - ``index`` (integer) -- the constraint's id. + + OUTPUT: + + A pair ``(indices, coeffs)`` where ``indices`` lists the + entries whose coefficient is nonzero, and to which ``coeffs`` + associates their coefficient on the model of the + ``add_linear_constraint`` method. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint( [(0, matrix([[33., -9.], [-9., 26.]])) , (1, matrix([[-7., -11.] ,[ -11., 3.]]) )]) + sage: p.row(0) + ([0, 1], + [ + [ 33.0000000000000 -9.00000000000000] + [-9.00000000000000 26.0000000000000], + + [-7.00000000000000 -11.0000000000000] + [-11.0000000000000 3.00000000000000] + ]) + """ + indices = [] + matrices = [] + for index,m in self.coeffs_matrix[i]: + if m != Matrix.zero(self.matrices_dim[i],self.matrices_dim[i]): + indices.append(index) + matrices.append(m) + return (indices, matrices) + + cpdef row_name(self, int index): + """ + Return the ``index`` th row name + + INPUT: + + - ``index`` (integer) -- the row's id + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_linear_constraints(1, names="A") + sage: p.row_name(0) + 'A' + + """ + if self.row_name_var[index] is not None: + return self.row_name_var[index] + return "constraint_" + repr(index) + + cpdef col_name(self, int index): + """ + Return the ``index`` th col name + + INPUT: + + - ``index`` (integer) -- the col's id + + - ``name`` (``char *``) -- its name. When set to ``NULL`` + (default), the method returns the current name. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_sdp_backend import get_solver + sage: p = get_solver(solver = "CVXOPT") + sage: p.add_variable(name="I am a variable") + 0 + sage: p.col_name(0) + 'I am a variable' + """ + if self.col_name_var[index] is not None: + return self.col_name_var[index] + return "x_" + repr(index) From d012c01bd36c9111d233889f809e35345d63ac59 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 2 Jul 2020 18:19:43 -0700 Subject: [PATCH 043/379] SemidefiniteProgram: New keyword argument base_ring, pass to backend --- .../numerical/backends/cvxopt_sdp_backend.pyx | 8 ++++++-- .../numerical/backends/generic_sdp_backend.pxd | 2 +- .../numerical/backends/generic_sdp_backend.pyx | 6 +++--- src/sage/numerical/sdp.pyx | 15 +++++++++++++-- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/sage/numerical/backends/cvxopt_sdp_backend.pyx b/src/sage/numerical/backends/cvxopt_sdp_backend.pyx index f783ae51f9e..84fb91d4536 100644 --- a/src/sage/numerical/backends/cvxopt_sdp_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_sdp_backend.pyx @@ -29,7 +29,7 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): cdef dict answer cdef dict param - def __init__(self, maximization=True): + def __init__(self, maximization=True, base_ring=None): """ Cython constructor @@ -41,7 +41,11 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): """ from sage.rings.all import RDF - MatrixSDPBackend.__init__(self, maximization, RDF) + if base_ring is None: + base_ring = RDF + if base_ring is not RDF: + raise ValueError("only base_ring=RDF is supported") + MatrixSDPBackend.__init__(self, maximization, base_ring=base_ring) self.param = {"show_progress":False, "maxiters":100, diff --git a/src/sage/numerical/backends/generic_sdp_backend.pxd b/src/sage/numerical/backends/generic_sdp_backend.pxd index a18a5a4b08b..58f9a1857d0 100644 --- a/src/sage/numerical/backends/generic_sdp_backend.pxd +++ b/src/sage/numerical/backends/generic_sdp_backend.pxd @@ -34,4 +34,4 @@ cdef class GenericSDPBackend: cpdef obj_constant_term cdef dict matrices_dim -cpdef GenericSDPBackend get_solver(solver = ?) +cpdef GenericSDPBackend get_solver(solver=?, base_ring=?) diff --git a/src/sage/numerical/backends/generic_sdp_backend.pyx b/src/sage/numerical/backends/generic_sdp_backend.pyx index 746fd27f6af..4ea0a4cea04 100644 --- a/src/sage/numerical/backends/generic_sdp_backend.pyx +++ b/src/sage/numerical/backends/generic_sdp_backend.pyx @@ -679,7 +679,7 @@ def default_sdp_solver(solver=None): else: raise ValueError("'solver' should be set to 'CVXOPT', a class, or None.") -cpdef GenericSDPBackend get_solver(solver = None): +cpdef GenericSDPBackend get_solver(solver=None, base_ring=None): """ Return a solver according to the given preferences. @@ -722,13 +722,13 @@ cpdef GenericSDPBackend get_solver(solver = None): solver = default_sdp_solver() if callable(solver): - return solver() + return solver(base_ring=base_ring) solver = solver.capitalize() if solver == "Cvxopt": from sage.numerical.backends.cvxopt_sdp_backend import CVXOPTSDPBackend - return CVXOPTSDPBackend() + return CVXOPTSDPBackend(base_ring=base_ring) else: raise ValueError("'solver' should be set to 'CVXOPT', a class, or None (in which case the default one is used).") diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index f69798888ec..62852636bdc 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -139,6 +139,15 @@ The default CVXOPT backend computes with the Real Double Field, for example:: sage: 0.5 + 3/2*x[1] 0.5 + 1.5*x_0 +For representing an SDP with exact data, there is another backend:: + + sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend + sage: p = SemidefiniteProgram(solver=MatrixSDPBackend, base_ring=QQ) + sage: p.base_ring() + Rational Field + sage: x = p.new_variable() + sage: 1/2 + 3/2 * x[1] + 1/2 + 3/2*x_0 Linear Variables and Expressions @@ -282,7 +291,7 @@ cdef class SemidefiniteProgram(SageObject): """ def __init__(self, solver=None, maximization=True, - names=tuple()): + names=tuple(), **kwds): r""" Constructor for the ``SemidefiniteProgram`` class. @@ -307,6 +316,8 @@ cdef class SemidefiniteProgram(SageObject): the SDP variables. Used to enable the ``sdp. = SemidefiniteProgram()`` syntax. + - other keyword arguments are passed to the solver. + .. SEEALSO:: - :meth:`default_sdp_solver` -- Returns/Sets the default SDP solver. @@ -318,7 +329,7 @@ cdef class SemidefiniteProgram(SageObject): """ self._first_variable_names = list(names) from sage.numerical.backends.generic_sdp_backend import get_solver - self._backend = get_solver(solver=solver) + self._backend = get_solver(solver=solver, **kwds) if not maximization: self._backend.set_sense(-1) From 4b848f5e93ffd4f7cfb80a1d851d61b3403b0814 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Fri, 3 Jul 2020 19:42:54 -0300 Subject: [PATCH 044/379] Split category files and other fixes * Fixed 1 docstring * Corrected pyflakes warnings * finitely generated LCA -> finitelyfreelygeneratedLCA Split off the files for finitely generated LCAs and LCAs with basis Do not force Graded() and Super() commuting with WithBasis() but still forcing Graded() and Super() to commute. --- .../algebras/lie_conformal_algebras.rst | 2 +- .../lie_conformal_algebras/examples.py | 4 + ...ra.py => finitely_freely_generated_lca.py} | 6 +- .../graded_lie_conformal_algebra.py | 6 +- .../lie_conformal_algebra.py | 6 +- ..._conformal_algebra_with_structure_coefs.py | 16 +- .../virasoro_lie_conformal_algebra.py | 2 +- ...nitely_generated_lie_conformal_algebras.py | 130 ++++++++ .../graded_lie_conformal_algebras.py | 158 ---------- src/sage/categories/lie_conformal_algebras.py | 284 ++++-------------- .../lie_conformal_algebras_with_basis.py | 217 +++++++++++++ src/sage/categories/super_modules.py | 1 + 12 files changed, 429 insertions(+), 403 deletions(-) rename src/sage/algebras/lie_conformal_algebras/{finitely_generated_lie_conformal_algebra.py => finitely_freely_generated_lca.py} (95%) create mode 100644 src/sage/categories/finitely_generated_lie_conformal_algebras.py delete mode 100644 src/sage/categories/graded_lie_conformal_algebras.py create mode 100644 src/sage/categories/lie_conformal_algebras_with_basis.py diff --git a/src/doc/en/reference/algebras/lie_conformal_algebras.rst b/src/doc/en/reference/algebras/lie_conformal_algebras.rst index d4c6c32f18f..0752d5843b7 100644 --- a/src/doc/en/reference/algebras/lie_conformal_algebras.rst +++ b/src/doc/en/reference/algebras/lie_conformal_algebras.rst @@ -29,7 +29,7 @@ See also .. toctree:: - ../sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca ../sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis diff --git a/src/sage/algebras/lie_conformal_algebras/examples.py b/src/sage/algebras/lie_conformal_algebras/examples.py index 1ab88d15e2a..06283e2cb9f 100644 --- a/src/sage/algebras/lie_conformal_algebras/examples.py +++ b/src/sage/algebras/lie_conformal_algebras/examples.py @@ -26,3 +26,7 @@ from .affine_lie_conformal_algebra import AffineLieConformalAlgebra as Affine from .neveu_schwarz_lie_conformal_algebra import NeveuSchwarzLieConformalAlgebra as NeveuSchwarz from .virasoro_lie_conformal_algebra import VirasoroLieConformalAlgebra as Virasoro + +assert Affine +assert NeveuSchwarz +assert Virasoro diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py similarity index 95% rename from src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py rename to src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py index 22b96a3a88c..32f9638ff3d 100644 --- a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py @@ -1,5 +1,5 @@ """ -Finitely Generated Lie Conformal Algebras. +Finitely and Freely Generated Lie Conformal Algebras. AUTHORS: @@ -21,7 +21,7 @@ from .freely_generated_lie_conformal_algebra import \ FreelyGeneratedLieConformalAlgebra -class FinitelyGeneratedLieConformalAlgebra(FreelyGeneratedLieConformalAlgebra): +class FinitelyFreelyGeneratedLCA(FreelyGeneratedLieConformalAlgebra): """ Abstract base class for finitely generated Lie conformal algebras. @@ -47,7 +47,7 @@ def __init__(self, R, index_set=None, central_elements=None, category=None, if index_set not in Sets().Finite(): raise TypeError("index_set must be a finite set") - super(FinitelyGeneratedLieConformalAlgebra,self).__init__(R, + super(FinitelyFreelyGeneratedLCA,self).__init__(R, index_set=index_set, central_elements=central_elements, category=category, element_class=element_class, prefix=prefix, **kwds) diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py index 2c903224c0f..34887fcdbe4 100644 --- a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -98,7 +98,7 @@ class GradedLieConformalAlgebra(LieConformalAlgebraWithStructureCoefficients): sage: K.degree() 0 sage: R.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + Category of H-graded finitely generated Lie conformal algebras with basis over Rational Field """ def __init__(self, R, s_coeff, index_set=None, central_elements=None, category=None, prefix=None, names=None, latex_names=None, @@ -111,8 +111,8 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, sage: V = lie_conformal_algebras.Virasoro(QQ) sage: TestSuite(V).run() """ - category = LieConformalAlgebras(R).Graded().WithBasis()\ - .FinitelyGenerated().or_subcategory(category) + category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated().\ + Graded().or_subcategory(category) element_class = GradedLCAElement LieConformalAlgebraWithStructureCoefficients.__init__(self,R, s_coeff,index_set=index_set,central_elements=central_elements, diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py index 2d7e1c7e519..0ec6ca765c3 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -151,7 +151,7 @@ sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) sage: V.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + Category of H-graded finitely generated Lie conformal algebras with basis over Rational Field sage: V.inject_variables() Defining a, b, K sage: a.bracket(b) @@ -273,7 +273,7 @@ class LieConformalAlgebra(UniqueRepresentation, Parent): sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} sage: V = LieConformalAlgebra(QQbar, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) sage: V.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Algebraic Field + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field sage: V.inject_variables() Defining a, b, K sage: a.bracket(b) @@ -292,7 +292,7 @@ class LieConformalAlgebra(UniqueRepresentation, Parent): sage: e.bracket(f.T()) {0: Th, 1: h, 2: 2*K} sage: V.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + Category of H-graded finitely generated Lie conformal algebras with basis over Rational Field sage: e.degree() 1 diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py index ac934107742..cc365a2eb9a 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -18,18 +18,16 @@ #***************************************************************************** from sage.functions.other import binomial -from sage.structure.indexed_generators import (IndexedGenerators, - standardize_names_index_set) from sage.sets.family import Family from .lie_conformal_algebra_element import LCAStructureCoefficientsElement from sage.categories.lie_conformal_algebras import LieConformalAlgebras - -from .finitely_generated_lie_conformal_algebra import \ - FinitelyGeneratedLieConformalAlgebra +from .finitely_freely_generated_lca import FinitelyFreelyGeneratedLCA from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.structure.indexed_generators import (IndexedGenerators, + standardize_names_index_set) class LieConformalAlgebraWithStructureCoefficients( - FinitelyGeneratedLieConformalAlgebra): + FinitelyFreelyGeneratedLCA): r""" A Lie conformal algebra with a set of specified structure coefficients. @@ -91,7 +89,7 @@ class LieConformalAlgebraWithStructureCoefficients( sage: betagamma_dict = {('b','a'):{0:{('K',0):1}}} sage: V = LieConformalAlgebra(QQ, betagamma_dict, names=('a','b'), weights=(1,0), central_elements=('K',)) sage: V.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + Category of H-graded finitely generated Lie conformal algebras with basis over Rational Field sage: V.inject_variables() Defining a, b, K sage: a.bracket(b) @@ -209,7 +207,6 @@ def _standardize_s_coeff(s_coeff, index_set, ce, parity=None): def __init__(self, R, s_coeff, index_set=None, central_elements=None, category=None, element_class=None, prefix=None, names=None, latex_names=None, parity=None, **kwds): - names, index_set = standardize_names_index_set(names,index_set) """ Initialize self. @@ -218,6 +215,7 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) sage: TestSuite(V).run() """ + names, index_set = standardize_names_index_set(names,index_set) if central_elements is None: central_elements= tuple() @@ -272,7 +270,7 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, if element_class is None: element_class=LCAStructureCoefficientsElement - FinitelyGeneratedLieConformalAlgebra.__init__( + FinitelyFreelyGeneratedLCA.__init__( self, R, index_set=index_set, central_elements=central_elements, category=category, element_class=element_class, prefix=prefix, names=names, latex_names=latex_names, **kwds) diff --git a/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py index 6d1c9d29b48..277b4082739 100644 --- a/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py @@ -39,7 +39,7 @@ class VirasoroLieConformalAlgebra(GradedLieConformalAlgebra): sage: Vir = lie_conformal_algebras.Virasoro(QQ) sage: Vir.category() - Category of finitely generated H-graded Lie conformal algebras with basis over Rational Field + Category of H-graded finitely generated Lie conformal algebras with basis over Rational Field sage: Vir.inject_variables() Defining L, C sage: L.bracket(L) diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py new file mode 100644 index 00000000000..89d5f25d2fa --- /dev/null +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -0,0 +1,130 @@ +""" +Finitely Generated Lie Conformal Algebras + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.misc.abstract_method import abstract_method +from sage.categories.graded_modules import GradedModulesCategory +from sage.categories.super_modules import SuperModulesCategory + +class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated() + Category of finitely generated Lie conformal algebras over Algebraic Field + """ + class ParentMethods: + def ngens(self): + r""" + The number of generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ngens() + 2 + + sage: V = lie_conformal_algebras.Affine(QQ, 'A2') + sage: V.ngens() + 9 + """ + return len(self.gens()) + + def gen(self,i): + r""" + The i-th generator of this Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: V.gen(0) + B[alpha[1]] + sage: V.1 + B[alphacheck[1]] + """ + return self.gens()[i] + + class Super(SuperModulesCategory): + """ + The subcategory of super finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).FinitelyGenerated().Super() + Category of super finitely generated Lie conformal algebras over Algebraic Real Field + """ + def extra_super_categories(self): + """ + The extra super categories of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated().Super().super_categories() + [Category of super Lie conformal algebras over Rational Field, + Category of finitely generated Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + class Graded(GradedModulesCategory): + """ + The subcategory of H-graded finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras over Algebraic Field + """ + + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras over Algebraic Field + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) + + class Super(SuperModulesCategory): + """ + The subcategory of super H-graded finitely generated Lie + conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras over Algebraic Field + """ + def extra_super_categories(self): + """ + The extra super categories of this category. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQ).FinitelyGenerated().Graded() + sage: C.super_categories() + [Category of H-graded Lie conformal algebras over Rational Field, + Category of finitely generated Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] diff --git a/src/sage/categories/graded_lie_conformal_algebras.py b/src/sage/categories/graded_lie_conformal_algebras.py deleted file mode 100644 index 6c38eb231fd..00000000000 --- a/src/sage/categories/graded_lie_conformal_algebras.py +++ /dev/null @@ -1,158 +0,0 @@ -r""" -Graded Lie Conformal Algebras - -A Lie conformal algebra `L` is called *H-Graded* [DSK2006]_ if there exists -a decomposition `L = \oplus L_n` such that the -`\lambda`-bracket becomes graded of degree `-1`, that is: - -.. MATH:: - - a_{(n)} b \in L_{p + q -n -1} \qquad - a \in L_p, \: b \in L_q, \: n \geq 0. - -In particular this implies that the action of `T` increases -degree by `1`. - -AUTHORS: - -- Reimundo Heluani (2019-10-05): Initial implementation. -""" - -#****************************************************************************** -# Copyright (C) 2019 Reimundo Heluani -# -# 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.categories.graded_modules import GradedModulesCategory -from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring -from sage.categories.super_modules import SuperModulesCategory -from sage.misc.abstract_method import abstract_method - -class GradedLieConformalAlgebras(GradedModulesCategory): - """ - The subcategory of H-graded Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Graded() - Category of H-graded Lie conformal algebras over Algebraic Field - """ - - def _repr_object_names(self): - """ - The names of the objects of this category - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Super().Graded() - Category of super H-graded Lie conformal algebras over Algebraic Field - sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() - Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring - """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) - - - class SubcategoryMethods: - - def Super(self, base_ring=None): - """ - The subcategory of H-graded super Lie conformal algebras - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).Super().Graded() - Category of super H-graded Lie conformal algebras over Rational Field - """ - assert base_ring is None or base_ring is self.base_ring() - if isinstance(self,CategoryWithAxiom_over_base_ring): - axioms_whitelist = frozenset(["WithBasis", - "FinitelyGeneratedAsLieConformalAlgebra"]) - axioms = axioms_whitelist.intersection(self.axioms()) - return self.base_category().Super()._with_axioms(axioms) - return SuperModulesCategory.category_of(self) - - class WithBasis(CategoryWithAxiom_over_base_ring): - """ - The subcategory of H-graded Lie conformal algebras with - basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(ZZ).Graded().WithBasis() - Category of H-graded Lie conformal algebras with basis over Integer Ring - """ - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated H-graded - Lie conformal algebras with basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(ZZ).Graded().FinitelyGenerated().WithBasis() - Category of finitely generated H-graded Lie conformal algebras with basis over Integer Ring - """ - pass - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated H-graded Lie - conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(ZZ).Graded().FinitelyGenerated(); C - Category of finitely generated H-graded Lie conformal algebras over Integer Ring - sage: C is LieConformalAlgebras(ZZ).FinitelyGenerated().Graded() - True - """ - pass - - class Super(SuperModulesCategory): - """ - The subcategory of H-graded super Lie conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(QQbar).Graded().Super(); C - Category of super H-graded Lie conformal algebras over Algebraic Field - sage: C is LieConformalAlgebras(QQbar).Super().Graded() - True - """ - def extra_super_categories(self): - """ - The extra super categories of this category. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super().super_categories() - [Category of finitely generated super Lie conformal algebras over Rational Field, - Category of finitely generated H-graded Lie conformal algebras over Rational Field, - Category of super H-graded Lie conformal algebras over Rational Field] - """ - return [self.base_category(),] - - - class ElementMethods: - @abstract_method - def degree(self): - """ - The degree of this element. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.degree() - 2 - sage: L.T(3).degree() - 5 - """ - raise NotImplementedError("Not implemented") diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index f70dab63b01..3271b1b7910 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -137,6 +137,8 @@ from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory from sage.categories.commutative_rings import CommutativeRings +from sage.misc.lazy_import import LazyImport + _CommutativeRings = CommutativeRings() class LieConformalAlgebras(Category_over_base_ring): @@ -174,14 +176,6 @@ class LieConformalAlgebras(Category_over_base_ring): That is, we only consider gradings on super Lie conformal algebras that are compatible with the `\mathbb{Z}/2\mathbb{Z}` grading. - All four subcategories commute in this sense:: - - sage: C = LieConformalAlgebras(AA).Graded().FinitelyGenerated().WithBasis().Super() - sage: D = LieConformalAlgebras(AA).Super().WithBasis().Graded().FinitelyGenerated() - sage: C is D - True - sage: C - Category of finitely generated super H-graded Lie conformal algebras with basis over Algebraic Real Field The base ring needs to be a commutative ring:: @@ -272,8 +266,6 @@ def _repr_object_names(self): sage: LieConformalAlgebras(QQ) Category of Lie conformal algebras over Rational Field - sage: LieConformalAlgebras(QQ).Graded().FinitelyGenerated() - Category of finitely generated H-graded Lie conformal algebras over Rational Field """ return "Lie conformal algebras over {}".format(self.base_ring()) @@ -365,14 +357,6 @@ def bracket(self,rhs): sage: E.bracket(F) {0: B[alphacheck[1]], 1: B['K']} - With a super Lie conformal algebra:: - - sage: R = lie_conformal_algebras.NeveuSchwarz(QQbar) - sage: R.inject_variables() - Defining L, G, C - sage: G.bracket(G.T()) - {0: 2*TL, 1: 2*L, 3: 2*C} - .. NOTE:: This method coerces both elements to the same parent @@ -412,7 +396,6 @@ def _bracket_(self,rhs): It is guaranteed that both are elements of the same parent. """ - raise NotImplementedError("Not implemented") @coerce_binop def nproduct(self,rhs,n): @@ -445,9 +428,6 @@ def _nproduct_(self,rhs,n): r""" The ``n``-th product of these two elements. - If `n\geq 0` it returns the element of this Lie conformal - algebra. - EXAMPLES:: sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 @@ -470,7 +450,7 @@ def _nproduct_(self,rhs,n): if n >= 0: return self.bracket(rhs).get(n,self.parent().zero()) else: - raise NotImplementedError("vertex algebras are not implemented yet") + raise NotImplementedError("vertex algebras are not implemented") @abstract_method def T(self, n=1): @@ -500,12 +480,19 @@ def T(self, n=1): 0 """ - @abstract_method def is_even_odd(self): """ Return ``0`` if this element is *even* and ``1`` if it is *odd*. + .. NOTE:: + + This method returns ``0`` by default since every Lie + conformal algebra can be thought as a purely even Lie + conformal algebra. In order to + implement a super Lie conformal algebra, the user + needs to implement this method. + EXAMPLES:: sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); @@ -514,14 +501,13 @@ def is_even_odd(self): sage: G.is_even_odd() 1 """ - raise NotImplementedError("Not Implemented") + return 0 class SubcategoryMethods: def FinitelyGeneratedAsLieConformalAlgebra(self): """ - The subcategory of finitely generated Lie conformal - algebras. + The subcategory of finitely generated Lie conformal algebras. EXAMPLES:: @@ -530,7 +516,6 @@ def FinitelyGeneratedAsLieConformalAlgebra(self): """ return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") - def FinitelyGenerated(self): """ The subcategory of finitely generated Lie conformal algebras. @@ -542,63 +527,24 @@ def FinitelyGenerated(self): """ return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") - def Graded(self, base_ring=None): - """ - The subcategory of H-Graded Lie conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(ZZ).WithBasis().Graded(); C - Category of H-graded Lie conformal algebras with basis over Integer Ring - sage: D = LieConformalAlgebras(ZZ).Graded().WithBasis() - sage: D is C - True - """ - assert base_ring is None or base_ring is self.base_ring() - if isinstance(self, CategoryWithAxiom_over_base_ring): - axioms_whitelist = frozenset(["WithBasis", - "FinitelyGeneratedAsLieConformalAlgebra"]) - axioms = axioms_whitelist.intersection(self.axioms()) - return self.base_category().Graded()._with_axioms(axioms) - return GradedModulesCategory.category_of(self) - - def Super(self, base_ring=None): - """ - The subcategory of super Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(AA).Super().WithBasis() - Category of super Lie conformal algebras with basis over Algebraic Real Field - """ - assert base_ring is None or base_ring is self.base_ring() - if isinstance(self,CategoryWithAxiom_over_base_ring): - axioms_whitelist = frozenset(["WithBasis", - "FinitelyGeneratedAsLieConformalAlgebra"]) - axioms = axioms_whitelist.intersection(self.axioms()) - return self.base_category().Super()._with_axioms(axioms) - return SuperModulesCategory.category_of(self) - class Super(SuperModulesCategory): """ The subcategory of super Lie conformal algebras. EXAMPLES:: - sage: LieConformalAlgebras(AA).Super().WithBasis() - Category of super Lie conformal algebras with basis over Algebraic Real Field + sage: LieConformalAlgebras(AA).Super() + Category of super Lie conformal algebras over Algebraic Real Field """ - #Need to do all this to make Super commute with Graded. def extra_super_categories(self): """ The extra super categories of this category EXAMPLES:: - sage: LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super().super_categories() - [Category of super H-graded Lie conformal algebras over Rational Field, - Category of finitely generated super Lie conformal algebras over Rational Field, - Category of finitely generated H-graded Lie conformal algebras over Rational Field] + sage: LieConformalAlgebras(QQ).Super().super_categories() + [Category of super modules over Rational Field, + Category of Lie conformal algebras over Rational Field] """ return [self.base_category(),] @@ -616,11 +562,9 @@ def example(self): return NeveuSchwarzLieConformalAlgebra(self.base_ring()) class SubcategoryMethods: - def Graded(self, base_ring=None): """ - The subcategory of H-graded super Lie conformal - algebras. + The subcategory of super H-graded Lie conformal algebras. EXAMPLES:: @@ -628,184 +572,74 @@ def Graded(self, base_ring=None): Category of super H-graded Lie conformal algebras over Rational Field """ assert base_ring is None or base_ring is self.base_ring() - if isinstance(self,CategoryWithAxiom_over_base_ring): - axioms_whitelist = frozenset(["WithBasis", - "FinitelyGeneratedAsLieConformalAlgebra"]) - axioms = axioms_whitelist.intersection(self.axioms()) - return self.base_category().Graded()._with_axioms(axioms) - return GradedModulesCategory.category_of( self.base_category()).Super() + class Graded(GradedModulesCategory): + """ + The subcategory of H-graded Lie conformal algebras. - class WithBasis(CategoryWithAxiom_over_base_ring): - """ - The subcategory of Super Lie conformal algebras with basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(ZZ).Super().WithBasis() - Category of super Lie conformal algebras with basis over Integer Ring - sage: LieConformalAlgebras(ZZ).Super().WithBasis() is LieConformalAlgebras(ZZ).WithBasis().Super() - True - """ - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated super Lie - conformal algebras with basis. - - EXAMPLES:: + EXAMPLES:: - sage: LieConformalAlgebras(ZZ).Super().FinitelyGenerated().WithBasis() - Category of finitely generated super Lie conformal algebras with basis over Integer Ring - """ - pass + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field + """ - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): + def _repr_object_names(self): """ - The subcategory of finitely generated super Lie - conformal algebras. + The names of the objects of this category EXAMPLES:: - sage: LieConformalAlgebras(ZZ).Super().FinitelyGenerated() - Category of finitely generated super Lie conformal algebras over Integer Ring + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field """ - pass + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) - from .graded_lie_conformal_algebras import GradedLieConformalAlgebras - Graded = GradedLieConformalAlgebras - class WithBasis(CategoryWithAxiom_over_base_ring): - """ - The subcategory of Lie conformal algebras with basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).WithBasis() - Category of Lie conformal algebras over Algebraic Field with basis - """ - def _repr_object_names(self): + class Super(SuperModulesCategory): """ - The names of objects of this category. + The subcategory of super H-graded Lie conformal algebras. EXAMPLES:: - sage: LieConformalAlgebras(QQ).WithBasis() - Category of Lie conformal algebras over Rational Field with basis + sage: C = LieConformalAlgebras(QQbar) + sage: C.Graded().Super() + Category of super H-graded Lie conformal algebras over Algebraic Field + sage: C.Graded().Super() is C.Super().Graded() + True """ - return "{} with basis".format(self.base_category().\ - _repr_object_names()) - - class ElementMethods: - - def index(self): + def extra_super_categories(self): """ - The index of this basis element. + The extra super categories of this category. EXAMPLES:: - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3).index() - ('G', 3) - sage: v = V.an_element(); v - L + G + C - sage: v.index() - Traceback (most recent call last): - ... - ValueError: index can only be computed for monomials, got L + G + C + sage: C = LieConformalAlgebras(QQ).Graded().Super() + sage: C.super_categories() + [Category of super Lie conformal algebras over Rational Field, + Category of H-graded Lie conformal algebras over Rational Field] """ - if self.is_zero(): - return None - if not self.is_monomial(): - raise ValueError ("index can only be computed for "\ - "monomials, got {}".format(self)) - - return next(iter(self.monomial_coefficients())) + return [self.base_category(),] - class SubcategoryMethods: - def FinitelyGenerated(self): + class ElementMethods: + @abstract_method + def degree(self): """ - The subcategory of finitely generated Lie conformal - algebras with basis. + The degree of this element. EXAMPLES:: - sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() - Category of finitely generated Lie conformal algebras with basis over Algebraic Field - """ - return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated Lie conformal - algebras with basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() - Category of finitely generated Lie conformal algebras with basis over Algebraic Field - """ - pass - - - class FinitelyGeneratedAsLieConformalAlgebra( - CategoryWithAxiom_over_base_ring): - """ - The subcategory of finitely generated Lie conformal - algebras with basis. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() - Category of finitely generated Lie conformal algebras with basis over Algebraic Field - """ - def _repr_object_names(self): - """ - The names of objects of this category. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).FinitelyGenerated() - Category of finitely generated Lie conformal algebras over Rational Field - """ - return "finitely generated {}".format(self.base_category().\ - _repr_object_names()) - - class ParentMethods: - def ngens(self): - r""" - The number of generators of this Lie conformal algebra. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ngens() + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.degree() 2 - - sage: V = lie_conformal_algebras.Affine(QQ, 'A2') - sage: V.ngens() - 9 + sage: L.T(3).degree() + 5 """ - return len(self.gens()) - def gen(self,i): - r""" - The i-th generator of this Lie conformal algebra. + WithBasis = LazyImport('sage.categories.lie_conformal_algebras_with_basis', + 'LieConformalAlgebrasWithBasis') - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: V.gen(0) - B[alpha[1]] - sage: V.1 - B[alphacheck[1]] - """ - return self.gens()[i] + FinitelyGeneratedAsLieConformalAlgebra = LazyImport( + 'sage.categories.finitely_generated_lie_conformal_algebras', + 'FinitelyGeneratedAsLieConformalAlgebra') diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py new file mode 100644 index 00000000000..41025f4a110 --- /dev/null +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -0,0 +1,217 @@ +""" +Lie Conformal Algebras With Basis + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.misc.abstract_method import abstract_method +from sage.categories.graded_modules import GradedModulesCategory +from sage.categories.super_modules import SuperModulesCategory + +class LieConformalAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): + """ + The subcategory of Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis() + Category of Lie conformal algebras with basis over Algebraic Field + """ + class ElementMethods: + + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for "\ + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + + class Super(SuperModulesCategory): + """ + The subcategory of super Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).WithBasis().Super() + Category of super Lie conformal algebras with basis over Algebraic Real Field + """ + def extra_super_categories(self): + """ + The extra super categories of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).WithBasis().Super().super_categories() + [Category of super modules with basis over Rational Field, + Category of Lie conformal algebras with basis over Rational Field, + Category of super Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + class Graded(GradedModulesCategory): + """ + The subcategory of H-graded Lie conformal algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().Graded() + Category of H-graded Lie conformal algebras with basis over Algebraic Field + """ + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().Graded() + Category of H-graded Lie conformal algebras with basis over Algebraic Field + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) + + class Super(SuperModulesCategory): + """ + The subcategory of super H-graded Lie conformal algebras + with basis. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar).WithBasis() + sage: C.Graded().Super() + Category of super H-graded Lie conformal algebras with basis over Algebraic Field + sage: C.Graded().Super() is C.Super().Graded() + True + """ + def extra_super_categories(self): + """ + The extra super categories of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).WithBasis().Graded().Super().super_categories() + [Category of super Lie conformal algebras with basis over Rational Field, + Category of H-graded Lie conformal algebras with basis over Rational Field, + Category of super H-graded Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): + """ + The subcategory of finitely generated Lie conformal + algebras with basis. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar) + sage: C.WithBasis().FinitelyGenerated() + Category of finitely generated Lie conformal algebras with basis over Algebraic Field + sage: C.WithBasis().FinitelyGenerated() is C.FinitelyGenerated().WithBasis() + True + """ + class Super(SuperModulesCategory): + """ + The subcategory of super finitely generated Lie conformal + algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).WithBasis().FinitelyGenerated().Super() + Category of super finitely generated Lie conformal algebras with basis over Algebraic Real Field + """ + def extra_super_categories(self): + """ + The extra super categories of this category + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQ).WithBasis().FinitelyGenerated().Super() + sage: C.super_categories() + [Category of super Lie conformal algebras with basis over Rational Field, + Category of finitely generated Lie conformal algebras with basis over Rational Field, + Category of super finitely generated Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + + class Graded(GradedModulesCategory): + """ + The subcategory of H-graded finitely generated Lie conformal + algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field + """ + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) + + class Super(SuperModulesCategory): + """ + The subcategory of super H-graded finitely generated + Lie conformal algebras with basis. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() + sage: C.Graded().Super() + Category of super H-graded finitely generated Lie conformal algebras with basis over Algebraic Field + sage: C.Graded().Super() is C.Super().Graded() + True + """ + def extra_super_categories(self): + """ + The extra super categories of this category. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super() + sage: C.super_categories() + [Category of super finitely generated Lie conformal algebras over Rational Field, + Category of super H-graded Lie conformal algebras over Rational Field, + Category of H-graded finitely generated Lie conformal algebras over Rational Field] + """ + return [self.base_category(),] + + diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index 7bdac10516a..b7f15241c86 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -15,6 +15,7 @@ # therefore the following whitelist. axiom_whitelist = frozenset(["Facade", "Finite", "Infinite", "FiniteDimensional", "Connected", "WithBasis", + "FinitelyGeneratedAsLieConformalAlgebra", # "Commutative", "Cocommutative", "Supercommutative", "Supercocommutative", "Associative", "Inverse", "Unital", "Division", From 44bd42c235303a35f051172520f5490b173569ec Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Fri, 3 Jul 2020 23:14:18 -0300 Subject: [PATCH 045/379] Fix errors introduced by rebase --- src/doc/en/reference/categories/index.rst | 3 ++- .../lie_conformal_algebras/lie_conformal_algebra_element.py | 1 - .../categories/finitely_generated_lie_conformal_algebras.py | 1 - src/sage/categories/lie_conformal_algebras.py | 1 - src/sage/categories/lie_conformal_algebras_with_basis.py | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index b4fd9823dde..7d91d976a68 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -95,6 +95,7 @@ Individual Categories sage/categories/finite_semigroups sage/categories/finite_sets sage/categories/finite_weyl_groups + sage/categories/finitely_generated_lie_conformal_algebras sage/categories/finitely_generated_magmas sage/categories/finitely_generated_semigroups sage/categories/function_fields @@ -111,7 +112,6 @@ Individual Categories sage/categories/graded_hopf_algebras_with_basis sage/categories/graded_lie_algebras sage/categories/graded_lie_algebras_with_basis - sage/categories/graded_lie_conformal_algebras sage/categories/graded_modules sage/categories/graded_modules_with_basis sage/categories/graphs @@ -132,6 +132,7 @@ Individual Categories sage/categories/lie_algebras sage/categories/lie_algebras_with_basis sage/categories/lie_conformal_algebras + sage/categories/lie_conformal_algebras_with_basis sage/categories/lie_groups sage/categories/loop_crystals sage/categories/l_trivial_semigroups diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index 9a02badc6c8..e5d13c38f51 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -19,7 +19,6 @@ from sage.functions.other import factorial from sage.misc.misc_c import prod -from sage.combinat.partition import Partition from sage.misc.misc import repr_lincomb from sage.misc.latex import latex from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 89d5f25d2fa..d58c0f4c93d 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -17,7 +17,6 @@ #***************************************************************************** from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring -from sage.misc.abstract_method import abstract_method from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index 3271b1b7910..b140c8ffdd5 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -130,7 +130,6 @@ from sage.categories.modules import Modules from .category_types import Category_over_base_ring -from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.structure.element import coerce_binop diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index 41025f4a110..d393b582c1c 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -17,7 +17,6 @@ #***************************************************************************** from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring -from sage.misc.abstract_method import abstract_method from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory From e64eb937074208ba78f8d581690ebbcaa24d9534 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sat, 4 Jul 2020 18:55:07 -0300 Subject: [PATCH 046/379] Added Jacobi test * Couldn't use directly tester as in ZZ._test_associativity because the testing utilities would not allow to modify the options parameter --- .../lie_conformal_algebra_element.py | 2 +- src/sage/categories/lie_conformal_algebras.py | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index e5d13c38f51..40a49bc2107 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -166,7 +166,7 @@ def _bracket_(self, right): ret = {} pz = p.zero() for d in diclist: - for k in d.keys(): + for k in d: ret[k] = ret.get(k,pz) + d[k] return {k:v for k,v in ret.items() if v} diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index b140c8ffdd5..59428580c12 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -329,6 +329,93 @@ def is_graded(self): """ return self in LieConformalAlgebras(self.base_ring()).Graded() + def is_finitely_generated(self): + """ + Whether this Lie conformal algebra is finitely generated. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.is_finitely_generated() + True + """ + return self in LieConformalAlgebras(self.base_ring()).FinitelyGenerated() + + def _test_jacobi(self, **options): + """ + Test the Jacobi condition on the generators of this Lie + conformal algebra. + + INPUT: + + - ``options`` -- any keyword arguments acceptde by :meth:`_tester` + + EXAMPLES: + + By default, on a finitely generated Lie conformal algebra, + this method tests all generators:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'B2') + sage: V._test_jacobi() + + It works for super Lie conformal algebras too:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V._test_jacobi() + + We can use specific elements by passing the ``elements`` + keyword argument:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.inject_variables() + Defining e, h, f, K + sage: V._test_jacobi(elements=(e, 2*f+h, 3*h)) + + TESTS:: + + sage: wrongdict = {('a', 'a'): {0: {('b', 0): 1}}, ('b', 'a'): {0: {('a', 0): 1}}} + sage: V = LieConformalAlgebra(QQ, wrongdict, names=('a', 'b'), parity=(1, 0)) + sage: V._test_jacobi() + Traceback (most recent call last): + ... + AssertionError + """ + elements = options.get('elements', None) + if elements is None: + if self.is_finitely_generated(): + elements = self.gens() + else: + elements = self.some_elements() + pz = self.zero() + tester = self._tester(**options) + from sage.misc.misc import some_tuples + from sage.functions.other import binomial + for x,y,z in some_tuples(elements, 3, tester._max_runs): + sgn = 1 + if self.is_super(): + if x.is_even_odd()*y.is_even_odd(): + sgn = -1 + brxy = x.bracket(y) + brxz = x.bracket(z) + bryz = y.bracket(z) + br1 = {k: x.bracket(v) for k,v in bryz.items()} + br2 = {k: v.bracket(z) for k,v in brxy.items()} + br3 = {k: y.bracket(v) for k,v in brxz.items()} + jac1 = {(j,k): v for k in br1 for j,v in br1[k].items()} + jac3 = {(k,j): v for k in br3 for j,v in br3[k].items()} + jac2 = {} + for k,br in br2.items(): + for j,v in br.items(): + for r in range(j+1): + jac2[(k+r, j-r)] = jac2.get((k+r, j-r), pz)\ + + binomial(k+r, r)*v + for k,v in jac2.items(): + jac1[k] = jac1.get(k, pz) - v + for k,v in jac3.items(): + jac1[k] = jac1.get(k, pz) - sgn*v + jacobiator = {k: v for k,v in jac1.items() if v} + assert not jacobiator + class ElementMethods: @coerce_binop def bracket(self,rhs): From 2ec6648cf0d24883ff1297d7a6d3982e566bcd2c Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sun, 5 Jul 2020 09:36:40 -0300 Subject: [PATCH 047/379] Revert _test_jacobi to some_elements instead of all gens * Even using self.gens() we may not get all the generators as Lie Conformal algebras (eg. if self is a PoissonVertexAlgebra). * Make sure that a finitely generated Lie conformal algebra returns at least all generators with `some_elements` --- ...nitely_generated_lie_conformal_algebras.py | 19 ++++++++ src/sage/categories/lie_conformal_algebras.py | 48 ++++++++++++++----- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index d58c0f4c93d..462c32677ae 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -62,6 +62,25 @@ def gen(self,i): """ return self.gens()[i] + def some_elements(self): + """ + Some elements of this Lie conformal algebra. + + Returns a list with elements containing at least the + generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.some_elements() + [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + class Super(SuperModulesCategory): """ The subcategory of super finitely generated Lie conformal algebras. diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index 59428580c12..46d961dd3cd 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -329,6 +329,18 @@ def is_graded(self): """ return self in LieConformalAlgebras(self.base_ring()).Graded() + def is_with_basis(self): + """ + Whether this Lie conformal algebra has a preferred basis. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.is_with_basis() + True + """ + return self in LieConformalAlgebras(self.base_ring()).WithBasis() + def is_finitely_generated(self): """ Whether this Lie conformal algebra is finitely generated. @@ -352,8 +364,8 @@ def _test_jacobi(self, **options): EXAMPLES: - By default, on a finitely generated Lie conformal algebra, - this method tests all generators:: + By default, this method tests only the elements returned by + ``self.some_elements()``:: sage: V = lie_conformal_algebras.Affine(QQ, 'B2') sage: V._test_jacobi() @@ -378,21 +390,31 @@ def _test_jacobi(self, **options): sage: V._test_jacobi() Traceback (most recent call last): ... - AssertionError + AssertionError: {(0, 0): -3*a} != {} + - {(0, 0): -3*a} + + {} """ - elements = options.get('elements', None) - if elements is None: - if self.is_finitely_generated(): - elements = self.gens() - else: - elements = self.some_elements() - pz = self.zero() tester = self._tester(**options) + S = tester.some_elements() + #Try our best to avoid non-homogeneous elements in super + #algebras: + if tester._instance.is_super(): + elements = [] + for s in S: + try: + s.is_even_odd() + except ValueError: + if tester._instance.is_with_basis(): + elements.extend(s.terms()) + continue + elements.append(s) + S = elements from sage.misc.misc import some_tuples from sage.functions.other import binomial - for x,y,z in some_tuples(elements, 3, tester._max_runs): + pz = tester._instance.zero() + for x,y,z in some_tuples(S, 3, tester._max_runs): sgn = 1 - if self.is_super(): + if tester._instance.is_super(): if x.is_even_odd()*y.is_even_odd(): sgn = -1 brxy = x.bracket(y) @@ -414,7 +436,7 @@ def _test_jacobi(self, **options): for k,v in jac3.items(): jac1[k] = jac1.get(k, pz) - sgn*v jacobiator = {k: v for k,v in jac1.items() if v} - assert not jacobiator + tester.assertDictEqual(jacobiator, {}) class ElementMethods: @coerce_binop From 3422bcde1b96078907e5ca1c01d08ea76e283c42 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sun, 5 Jul 2020 18:07:08 -0300 Subject: [PATCH 048/379] Docstring fixing * A full stop * Returns -> This method returns --- .../categories/finitely_generated_lie_conformal_algebras.py | 4 ++-- src/sage/categories/lie_conformal_algebras_with_basis.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 462c32677ae..832b471e826 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -66,8 +66,8 @@ def some_elements(self): """ Some elements of this Lie conformal algebra. - Returns a list with elements containing at least the - generators. + This method returns a list with elements containing at + least the generators. EXAMPLES:: diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index d393b582c1c..f4e37507c44 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -90,15 +90,14 @@ class Graded(GradedModulesCategory): """ def _repr_object_names(self): """ - The names of the objects of this category + The names of the objects of this category. EXAMPLES:: sage: LieConformalAlgebras(QQbar).WithBasis().Graded() Category of H-graded Lie conformal algebras with basis over Algebraic Field """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) + return "H-graded {}".format(self.base_category()._repr_object_names()) class Super(SuperModulesCategory): """ From f668b8e0e00b43e1a22aebab64fbcf289add72a7 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Wed, 8 Jul 2020 13:18:05 -0300 Subject: [PATCH 049/379] Rewiewer fixes --- ...nitely_generated_lie_conformal_algebras.py | 9 +- src/sage/categories/lie_conformal_algebras.py | 100 ++++++++---------- .../lie_conformal_algebras_with_basis.py | 20 ++-- 3 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 832b471e826..8764cea01a8 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -22,7 +22,7 @@ class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): """ - The subcategory of finitely generated Lie conformal algebras. + The category of finitely generated Lie conformal algebras. EXAMPLES:: @@ -48,7 +48,7 @@ def ngens(self): def gen(self,i): r""" - The i-th generator of this Lie conformal algebra. + The ``i``-th generator of this Lie conformal algebra. EXAMPLES:: @@ -83,7 +83,7 @@ def some_elements(self): class Super(SuperModulesCategory): """ - The subcategory of super finitely generated Lie conformal algebras. + The category of super finitely generated Lie conformal algebras. EXAMPLES:: @@ -104,14 +104,13 @@ def extra_super_categories(self): class Graded(GradedModulesCategory): """ - The subcategory of H-graded finitely generated Lie conformal algebras. + The category of H-graded finitely generated Lie conformal algebras. EXAMPLES:: sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() Category of H-graded finitely generated Lie conformal algebras over Algebraic Field """ - def _repr_object_names(self): """ The names of the objects of this category diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index 46d961dd3cd..b25c179ea9d 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -25,8 +25,7 @@ where `p(a)` is `0` if `a` is *even* and `1` if `a` is *odd*. The bracket in the RHS is computed as follows. First we evaluate - `[b_\mu a]` with the formal - parameter `\mu` to the *left*, then + `[b_\mu a]` with the formal parameter `\mu` to the *left*, then replace each appearance of the formal variable `\mu` by `-\lambda - T`. Finally apply `T` to the coefficients in `L`. @@ -35,13 +34,13 @@ .. MATH:: [a_\lambda [b_\mu c]] = [ [a_{\lambda + \mu} b]_\mu c] + - (-1)^{p(a)p(b)} [b_\mu [a_\lambda c ]], + (-1)^{p(a)p(b)} [b_\mu [a_\lambda c]], - which is understood as an equality in `L[\lambda,\mu]`. + which is understood as an equality in `L[\lambda, \mu]`. `T` is usually called the *translation operation* or the *derivative*. For an element `a \in L` we will say that `Ta` is the *derivative of* - `a`. We define the *n-th products* `a_{(n)} b` for `a,b \in L` by + `a`. We define the `n`-*th products* `a_{(n)} b` for `a,b \in L` by .. MATH:: @@ -174,7 +173,7 @@ class LieConformalAlgebras(Category_over_base_ring): True That is, we only consider gradings on super Lie conformal algebras - that are compatible with the `\mathbb{Z}/2\mathbb{Z}` grading. + that are compatible with the `\ZZ/2\ZZ` grading. The base ring needs to be a commutative ring:: @@ -184,13 +183,13 @@ class LieConformalAlgebras(Category_over_base_ring): """ @staticmethod - def __classcall_private__(cls,R,check=True): + def __classcall_private__(cls, R, check=True): r""" INPUT: - - `R` -- a commutative ring. + - `R` -- a commutative ring - ``check`` -- a boolean (default: ``True``); whether to check - that `R` is a commutative ring. + that `R` is a commutative ring EXAMPLES:: @@ -202,8 +201,7 @@ def __classcall_private__(cls,R,check=True): """ if check: if not (R in _CommutativeRings): - raise ValueError("base must be a commutative ring got {}"\ - .format(R)) + raise ValueError("base must be a commutative ring got {}".format(R)) return super(LieConformalAlgebras,cls).__classcall__(cls,R) @cached_method @@ -271,13 +269,12 @@ def _repr_object_names(self): class ParentMethods: def ideal(self, *gens, **kwds): - """ + r""" The ideal of this conformal algebra generated by ``gens``. .. TODO:: - Ideals of Lie Conformal Algebras are not implemented - yet. + Ideals of Lie Conformal Algebras are not implemented yet. EXAMPLES:: @@ -287,7 +284,7 @@ def ideal(self, *gens, **kwds): ... NotImplementedError: ideals of Lie Conformal algebras are not implemented yet """ - raise NotImplementedError("ideals of Lie Conformal algebras are "\ + raise NotImplementedError("ideals of Lie Conformal algebras are " "not implemented yet") def is_super(self): @@ -307,7 +304,8 @@ def is_super(self): conformal algebra:: sage: bosondict = {('a','a'):{1:{('K',0):1}}} - sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',),central_elements=('K',),super=True) + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',), + ....: central_elements=('K',),super=True) sage: R.is_super() True sage: [g.is_even_odd() for g in R.gens()] @@ -317,7 +315,7 @@ def is_super(self): def is_graded(self): """ - Wether this Lie conformal algebra is graded or not + Wether this Lie conformal algebra is graded or not. EXAMPLES:: @@ -440,13 +438,13 @@ def _test_jacobi(self, **options): class ElementMethods: @coerce_binop - def bracket(self,rhs): + def bracket(self, rhs): r""" The `\lambda`-bracket of these two elements. EXAMPLES: - The brackets of the Virasoro Lie conformal Algebra:: + The brackets of the Virasoro Lie conformal algebra:: sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 sage: L.bracket(L) @@ -464,20 +462,19 @@ def bracket(self,rhs): {1: 2*B['K']} sage: E.bracket(F) {0: B[alphacheck[1]], 1: B['K']} - - .. NOTE:: - - This method coerces both elements to the same parent - in order to implement a Lie conformal algebra the user - needs to implement :meth:`_bracket_` """ return self._bracket_(rhs) @abstract_method - def _bracket_(self,rhs): + def _bracket_(self, rhs): r""" The `\lambda`-bracket of these two elements. + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + EXAMPLES: The brackets of the Virasoro Lie conformal Algebra:: @@ -498,44 +495,38 @@ def _bracket_(self,rhs): {1: 2*B['K']} sage: E._bracket_(F) {0: B[alphacheck[1]], 1: B['K']} - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. """ @coerce_binop - def nproduct(self,rhs,n): + def nproduct(self, rhs, n): r""" The ``n``-th product of these two elements. EXAMPLES:: sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.nproduct(L,3) + sage: L.nproduct(L, 3) 1/2*C - sage: L.nproduct(L.T(),0) + sage: L.nproduct(L.T(), 0) 2*T^(2)L sage: V = lie_conformal_algebras.Affine(QQ, 'A1') sage: E = V.0; H = V.1; F = V.2; - sage: E.nproduct(H,0) == - 2*E + sage: E.nproduct(H, 0) == - 2*E True - sage: E.nproduct(F,1) + sage: E.nproduct(F, 1) B['K'] - - .. NOTE:: - - This method coerces both elements to the same parent - in order to implement a Lie conformal algebra the user - needs to implement :meth:`_nproduct_` """ return self._nproduct_(rhs,n) - def _nproduct_(self,rhs,n): + def _nproduct_(self, rhs, n): r""" The ``n``-th product of these two elements. + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + EXAMPLES:: sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 @@ -549,11 +540,6 @@ def _nproduct_(self,rhs,n): True sage: E._nproduct_(F,1) B['K'] - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. """ if n >= 0: return self.bracket(rhs).get(n,self.parent().zero()) @@ -563,17 +549,17 @@ def _nproduct_(self,rhs,n): @abstract_method def T(self, n=1): r""" - The n-th derivative of this element. + The ``n``-th derivative of ``self``. INPUT: - ``n`` -- integer (default:``1``); how many times - to apply `T` to this element. + to apply `T` to this element OUTPUT: - `T^n a` where `a` is this element. Notice that We use the - *divided powers* notation `T^{(j)} = \frac{T^j}{j!}` + `T^n a` where `a` is this element. Notice that we use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. EXAMPLES:: @@ -615,7 +601,7 @@ class SubcategoryMethods: def FinitelyGeneratedAsLieConformalAlgebra(self): """ - The subcategory of finitely generated Lie conformal algebras. + The category of finitely generated Lie conformal algebras. EXAMPLES:: @@ -626,7 +612,7 @@ def FinitelyGeneratedAsLieConformalAlgebra(self): def FinitelyGenerated(self): """ - The subcategory of finitely generated Lie conformal algebras. + The category of finitely generated Lie conformal algebras. EXAMPLES:: @@ -637,7 +623,7 @@ def FinitelyGenerated(self): class Super(SuperModulesCategory): """ - The subcategory of super Lie conformal algebras. + The category of super Lie conformal algebras. EXAMPLES:: @@ -646,7 +632,7 @@ class Super(SuperModulesCategory): """ def extra_super_categories(self): """ - The extra super categories of this category + The extra super categories of this category. EXAMPLES:: @@ -672,7 +658,7 @@ def example(self): class SubcategoryMethods: def Graded(self, base_ring=None): """ - The subcategory of super H-graded Lie conformal algebras. + The category of super H-graded Lie conformal algebras. EXAMPLES:: diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index f4e37507c44..d51e254e1c9 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -22,7 +22,7 @@ class LieConformalAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ - The subcategory of Lie conformal algebras with basis. + The category of Lie conformal algebras with basis. EXAMPLES:: @@ -52,14 +52,14 @@ def index(self): if self.is_zero(): return None if not self.is_monomial(): - raise ValueError ("index can only be computed for "\ + raise ValueError ("index can only be computed for " "monomials, got {}".format(self)) return next(iter(self.monomial_coefficients())) class Super(SuperModulesCategory): """ - The subcategory of super Lie conformal algebras with basis. + The category of super Lie conformal algebras with basis. EXAMPLES:: @@ -68,7 +68,7 @@ class Super(SuperModulesCategory): """ def extra_super_categories(self): """ - The extra super categories of this category + The extra super categories of this category. EXAMPLES:: @@ -81,7 +81,7 @@ def extra_super_categories(self): class Graded(GradedModulesCategory): """ - The subcategory of H-graded Lie conformal algebras with basis. + The category of H-graded Lie conformal algebras with basis. EXAMPLES:: @@ -101,7 +101,7 @@ def _repr_object_names(self): class Super(SuperModulesCategory): """ - The subcategory of super H-graded Lie conformal algebras + The category of super H-graded Lie conformal algebras with basis. EXAMPLES:: @@ -127,7 +127,7 @@ def extra_super_categories(self): class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): """ - The subcategory of finitely generated Lie conformal + The category of finitely generated Lie conformal algebras with basis. EXAMPLES:: @@ -140,7 +140,7 @@ class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): """ class Super(SuperModulesCategory): """ - The subcategory of super finitely generated Lie conformal + The category of super finitely generated Lie conformal algebras with basis. EXAMPLES:: @@ -165,7 +165,7 @@ def extra_super_categories(self): class Graded(GradedModulesCategory): """ - The subcategory of H-graded finitely generated Lie conformal + The category of H-graded finitely generated Lie conformal algebras with basis. EXAMPLES:: @@ -187,7 +187,7 @@ def _repr_object_names(self): class Super(SuperModulesCategory): """ - The subcategory of super H-graded finitely generated + The category of super H-graded finitely generated Lie conformal algebras with basis. EXAMPLES:: From cbd2319dd12d941deede5b1550e6e5b686f2468a Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Wed, 8 Jul 2020 16:04:18 -0300 Subject: [PATCH 050/379] Make super Lie Conformal Algebras an independent category * LieConformalAlgebras().Super() is not a subcategory of LieConformalAlgebras() * LCA.Super().Graded() is a proper subcategory and equals LCA.Graded().Super() * Simplified the element classes by using methods from FilteredModulesWithBasis and SuperModulesWithBasis --- .../finitely_freely_generated_lca.py | 7 +- .../graded_lie_conformal_algebra.py | 13 +- .../lie_conformal_algebra_element.py | 55 --- .../lie_conformal_algebra_with_basis.py | 7 +- ..._conformal_algebra_with_structure_coefs.py | 7 +- ...nitely_generated_lie_conformal_algebras.py | 90 +++-- src/sage/categories/lie_conformal_algebras.py | 375 +++++++++++++++--- .../lie_conformal_algebras_with_basis.py | 174 +++++--- 8 files changed, 506 insertions(+), 222 deletions(-) diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py index 32f9638ff3d..b204c4a89b7 100644 --- a/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +++ b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py @@ -40,8 +40,11 @@ def __init__(self, R, index_set=None, central_elements=None, category=None, sage: V = lie_conformal_algebras.Virasoro(QQ) sage: TestSuite(V).run() """ - category = LieConformalAlgebras(R).FinitelyGenerated().or_subcategory( - category) + default_category = LieConformalAlgebras(R).FinitelyGenerated() + try: + category = default_category.or_subcategory(category) + except ValueError: + category = default_category.Super().or_subcategory(category) from sage.categories.sets_cat import Sets if index_set not in Sets().Finite(): diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py index 34887fcdbe4..9f4500d28b4 100644 --- a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -47,7 +47,6 @@ from sage.categories.lie_conformal_algebras import LieConformalAlgebras -from .lie_conformal_algebra_element import GradedLCAElement from .lie_conformal_algebra_with_structure_coefs import \ LieConformalAlgebraWithStructureCoefficients @@ -111,12 +110,16 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, sage: V = lie_conformal_algebras.Virasoro(QQ) sage: TestSuite(V).run() """ - category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated().\ - Graded().or_subcategory(category) - element_class = GradedLCAElement + is_super = kwds.get('super',None) + default_category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated().Graded() + if is_super or parity: + category = default_category.Super().or_subcategory(category) + else: + category = default_category.or_subcategory(category) + LieConformalAlgebraWithStructureCoefficients.__init__(self,R, s_coeff,index_set=index_set,central_elements=central_elements, - category=category, element_class=element_class, prefix=prefix, + category=category, prefix=prefix, names=names, latex_names=latex_names, parity=parity, **kwds) if weights is None: diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index 40a49bc2107..de30de070f6 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -94,29 +94,6 @@ class LCAStructureCoefficientsElement(LCAWithGeneratorsElement): An element of a Lie conformal algebra given by structure coefficients. """ - - def is_even_odd(self): - """ - Return ``0`` if this element is even or ``1`` if it is odd. - - EXAMPLES:: - - sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); R.inject_variables() - Defining L, G, C - sage: L.is_even_odd() - 0 - sage: G.is_odd() - True - """ - if self.is_zero(): - return 0 - p = self.parent() - coefs = self.monomial_coefficients() - paritylist = [p._parity[p.monomial((k,0))] for k,v in coefs] - if paritylist[1:] == paritylist[:-1]: - return paritylist[0] - raise ValueError("{} is not homogeneous".format(self)) - def _bracket_(self, right): """ The lambda bracket of these two elements. @@ -260,35 +237,3 @@ def _latex_(self): return repr_lincomb(terms, is_latex=True, strip_one = True) -class GradedLCAElement(LCAStructureCoefficientsElement): - """ - Base class for H-graded Lie conformal algebras with structure - coefficients. - """ - def degree(self): - """ - The degree of this element. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.inject_variables() - Defining L, C - sage: C.degree() - 0 - sage: L.T(4).degree() - 6 - """ - if self.is_zero(): - from sage.rings.infinity import Infinity - return Infinity - p = self.parent() - ls = [] - for gen,n in self.monomial_coefficients(): - ret = n - if gen not in p._central_elements: - ret+= p._weights[p._index_to_pos[gen]] - ls.append(ret) - if ls[1:] == ls[:-1]: - return ls[0] - raise ValueError("{} is not homogeneous".format(self)) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py index af574531100..e56785601fe 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py @@ -66,7 +66,12 @@ def __init__(self,R, basis_keys=None, element_class=None, category=None, kwds['bracket'] = '' kwds['string_quotes'] = False - category = LieConformalAlgebras(R).WithBasis().or_subcategory(category) + default_category = LieConformalAlgebras(R).WithBasis() + try: + category = default_category.or_subcategory(category) + except ValueError: + category = default_category.Super().or_subcategory(category) + super(LieConformalAlgebraWithBasis,self).__init__(R, basis_keys=basis_keys, element_class=element_class, category=category, prefix=prefix, names=None, **kwds) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py index cc365a2eb9a..7d56e697aa2 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -262,10 +262,11 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, for i,ce in enumerate(central_elements): self._index_to_pos[ce] = len(index_set)+i - category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated()\ - .or_subcategory(category) + default_category = LieConformalAlgebras(R).WithBasis().FinitelyGenerated() if issuper: - category = category.Super() + category = default_category.Super().or_subcategory(category) + else: + category = default_category.or_subcategory(category) if element_class is None: element_class=LCAStructureCoefficientsElement diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 8764cea01a8..90a38209b02 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -90,17 +90,72 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).FinitelyGenerated().Super() Category of super finitely generated Lie conformal algebras over Algebraic Real Field """ - def extra_super_categories(self): + class ParentMethods: + def ngens(self): + r""" + The number of generators of this super Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.ngens() + 3 + """ + return len(self.gens()) + + def gen(self,i): + r""" + The ``i``-th generator of this super Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.gens() + (L, G, C) + sage: V.gen(0) + L + """ + return self.gens()[i] + + def some_elements(self): + """ + Some elements of this super Lie conformal algebra. + + This method returns a list with elements containing at + least the generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.some_elements() + [L, G, C, TG + 4*T^(2)L, TG + 4*T^(2)G, TL + 4*T^(2)L] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + + class Graded(GradedModulesCategory): """ - The extra super categories of this category + The category of H-graded super finitely generated Lie conformal algebras. EXAMPLES:: - sage: LieConformalAlgebras(QQ).FinitelyGenerated().Super().super_categories() - [Category of super Lie conformal algebras over Rational Field, - Category of finitely generated Lie conformal algebras over Rational Field] + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Super().Graded() + Category of H-graded super finitely generated Lie conformal algebras over Algebraic Field """ - return [self.base_category(),] + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Super().Graded() + Category of H-graded super finitely generated Lie conformal algebras over Algebraic Field + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) class Graded(GradedModulesCategory): """ @@ -122,26 +177,3 @@ def _repr_object_names(self): """ return "H-graded {}".format(self.base_category().\ _repr_object_names()) - - class Super(SuperModulesCategory): - """ - The subcategory of super H-graded finitely generated Lie - conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() - Category of H-graded finitely generated Lie conformal algebras over Algebraic Field - """ - def extra_super_categories(self): - """ - The extra super categories of this category. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(QQ).FinitelyGenerated().Graded() - sage: C.super_categories() - [Category of H-graded Lie conformal algebras over Rational Field, - Category of finitely generated Lie conformal algebras over Rational Field] - """ - return [self.base_category(),] diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index b25c179ea9d..ec4d4eb24d7 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -168,7 +168,7 @@ class LieConformalAlgebras(Category_over_base_ring): ``Super``. These functors commute:: sage: LieConformalAlgebras(AA).Graded().Super() - Category of super H-graded Lie conformal algebras over Algebraic Real Field + Category of H-graded super Lie conformal algebras over Algebraic Real Field sage: LieConformalAlgebras(AA).Graded().Super() is LieConformalAlgebras(AA).Super().Graded() True @@ -270,7 +270,7 @@ class ParentMethods: def ideal(self, *gens, **kwds): r""" - The ideal of this conformal algebra generated by ``gens``. + The ideal of this Lie conformal algebra generated by ``gens``. .. TODO:: @@ -353,8 +353,7 @@ def is_finitely_generated(self): def _test_jacobi(self, **options): """ - Test the Jacobi condition on the generators of this Lie - conformal algebra. + Test the Jacobi axiom of this Lie conformal algebra. INPUT: @@ -366,7 +365,7 @@ def _test_jacobi(self, **options): ``self.some_elements()``:: sage: V = lie_conformal_algebras.Affine(QQ, 'B2') - sage: V._test_jacobi() + sage: V._test_jacobi() # long time (6 seconds) It works for super Lie conformal algebras too:: @@ -394,27 +393,10 @@ def _test_jacobi(self, **options): """ tester = self._tester(**options) S = tester.some_elements() - #Try our best to avoid non-homogeneous elements in super - #algebras: - if tester._instance.is_super(): - elements = [] - for s in S: - try: - s.is_even_odd() - except ValueError: - if tester._instance.is_with_basis(): - elements.extend(s.terms()) - continue - elements.append(s) - S = elements from sage.misc.misc import some_tuples from sage.functions.other import binomial pz = tester._instance.zero() for x,y,z in some_tuples(S, 3, tester._max_runs): - sgn = 1 - if tester._instance.is_super(): - if x.is_even_odd()*y.is_even_odd(): - sgn = -1 brxy = x.bracket(y) brxz = x.bracket(z) bryz = y.bracket(z) @@ -432,7 +414,7 @@ def _test_jacobi(self, **options): for k,v in jac2.items(): jac1[k] = jac1.get(k, pz) - v for k,v in jac3.items(): - jac1[k] = jac1.get(k, pz) - sgn*v + jac1[k] = jac1.get(k, pz) - v jacobiator = {k: v for k,v in jac1.items() if v} tester.assertDictEqual(jacobiator, {}) @@ -630,18 +612,6 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).Super() Category of super Lie conformal algebras over Algebraic Real Field """ - def extra_super_categories(self): - """ - The extra super categories of this category. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).Super().super_categories() - [Category of super modules over Rational Field, - Category of Lie conformal algebras over Rational Field] - """ - return [self.base_category(),] - def example(self): """ An example parent in this category. @@ -655,19 +625,309 @@ def example(self): import NeveuSchwarzLieConformalAlgebra return NeveuSchwarzLieConformalAlgebra(self.base_ring()) - class SubcategoryMethods: - def Graded(self, base_ring=None): + class ParentMethods: + + def ideal(self, *gens, **kwds): + r""" + The ideal of this super Lie conformal algebra generated + by ``gens``. + + .. TODO:: + + Ideals of super Lie Conformal Algebras are not + implemented yet. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.ideal() + Traceback (most recent call last): + ... + NotImplementedError: ideals of super Lie Conformal algebras are not implemented yet """ - The category of super H-graded Lie conformal algebras. + raise NotImplementedError("ideals of super Lie Conformal algebras " + "are not implemented yet") + + def is_super(self): + """ + This method returns ``True``. EXAMPLES:: - sage: LieConformalAlgebras(QQ).Super().Graded() - Category of super H-graded Lie conformal algebras over Rational Field + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.is_super() + False + sage: lie_conformal_algebras.NeveuSchwarz(QQbar).is_super() + True + + Notice that we can force to have a *purely even* super Lie + conformal algebra:: + + sage: bosondict = {('a','a'):{1:{('K',0):1}}} + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',), + ....: central_elements=('K',),super=True) + sage: R.is_super() + True + sage: [g.is_even_odd() for g in R.gens()] + [0, 0] """ - assert base_ring is None or base_ring is self.base_ring() - return GradedModulesCategory.category_of( - self.base_category()).Super() + return True + + def is_graded(self): + """ + Wether this super Lie conformal algebra is graded or not. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V + The Neveu-Schwarz super Lie conformal algebra over Rational Field + sage: V.is_graded() + True + """ + return self in LieConformalAlgebras(self.base_ring()).Graded().Super() + + def is_with_basis(self): + """ + Whether this super Lie conformal algebra has a preferred + basis by homogeneous elements. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.is_with_basis() + True + """ + return self in LieConformalAlgebras(self.base_ring()).WithBasis().Super() + + def is_finitely_generated(self): + """ + Whether this super Lie conformal algebra is finitely generated. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.is_finitely_generated() + True + """ + return self in LieConformalAlgebras(self.base_ring()).FinitelyGenerated().Super() + + def _test_jacobi(self, **options): + """ + Test the Jacobi axiom of this super Lie conformal algebra. + + INPUT: + + - ``options`` -- any keyword arguments acceptde by :meth:`_tester` + + EXAMPLES: + + By default, this method tests only the elements returned by + ``self.some_elements()``:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'B2') + sage: V._test_jacobi() # long time (6 seconds) + + It works for super Lie conformal algebras too:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V._test_jacobi() + + We can use specific elements by passing the ``elements`` + keyword argument:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.inject_variables() + Defining e, h, f, K + sage: V._test_jacobi(elements=(e, 2*f+h, 3*h)) + + TESTS:: + + sage: wrongdict = {('a', 'a'): {0: {('b', 0): 1}}, ('b', 'a'): {0: {('a', 0): 1}}} + sage: V = LieConformalAlgebra(QQ, wrongdict, names=('a', 'b'), parity=(1, 0)) + sage: V._test_jacobi() + Traceback (most recent call last): + ... + AssertionError: {(0, 0): -3*a} != {} + - {(0, 0): -3*a} + + {} + """ + tester = self._tester(**options) + S = tester.some_elements() + #Try our best to avoid non-homogeneous elements + elements = [] + for s in S: + try: + s.is_even_odd() + except ValueError: + if tester._instance.is_with_basis(): + elements.extend([s.even_component(),s.odd_component()]) + continue + elements.append(s) + S = elements + from sage.misc.misc import some_tuples + from sage.functions.other import binomial + pz = tester._instance.zero() + for x,y,z in some_tuples(S, 3, tester._max_runs): + if x.is_even_odd()*y.is_even_odd(): + sgn = -1 + else: + sgn = 1 + brxy = x.bracket(y) + brxz = x.bracket(z) + bryz = y.bracket(z) + br1 = {k: x.bracket(v) for k,v in bryz.items()} + br2 = {k: v.bracket(z) for k,v in brxy.items()} + br3 = {k: y.bracket(v) for k,v in brxz.items()} + jac1 = {(j,k): v for k in br1 for j,v in br1[k].items()} + jac3 = {(k,j): v for k in br3 for j,v in br3[k].items()} + jac2 = {} + for k,br in br2.items(): + for j,v in br.items(): + for r in range(j+1): + jac2[(k+r, j-r)] = jac2.get((k+r, j-r), pz)\ + + binomial(k+r, r)*v + for k,v in jac2.items(): + jac1[k] = jac1.get(k, pz) - v + for k,v in jac3.items(): + jac1[k] = jac1.get(k, pz) - sgn*v + jacobiator = {k: v for k,v in jac1.items() if v} + tester.assertDictEqual(jacobiator, {}) + + class ElementMethods: + @coerce_binop + def bracket(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + EXAMPLES: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.bracket(G.T()) + {0: 2*TL, 1: 2*L, 3: 2*C} + """ + return self._bracket_(rhs) + + @abstract_method + def _bracket_(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G._bracket_(G.T()) + {0: 2*TL, 1: 2*L, 3: 2*C} + """ + + @coerce_binop + def nproduct(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.nproduct(G.T(), 1) + 2*L + """ + return self._nproduct_(rhs, n) + + def _nproduct_(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.nproduct(G.T(), 1) + 2*L + """ + if n >= 0: + return self.bracket(rhs).get(n,self.parent().zero()) + else: + raise NotImplementedError("super vertex algebras are not implemented") + + @abstract_method + def T(self, n=1): + r""" + The ``n``-th derivative of ``self``. + + INPUT: + + - ``n`` -- integer (default:``1``); how many times + to apply `T` to this element + + OUTPUT: + + `T^n a` where `a` is this element. Notice that we use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3) + 6*T^(3)G + """ + + @abstract_method + def is_even_odd(self): + """ + Return ``0`` if this element is *even* and ``1`` if it is + *odd*. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); + sage: R.inject_variables() + Defining L, G, C + sage: G.is_even_odd() + 1 + """ + + class Graded(GradedModulesCategory): + """ + The subcategory of H-graded super Lie conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar) + sage: C.Graded().Super() + Category of H-graded super Lie conformal algebras over Algebraic Field + sage: C.Graded().Super() is C.Super().Graded() + True + """ + def _repr_object_names(self): + """ + The names of the objects of this category + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) class Graded(GradedModulesCategory): """ @@ -678,7 +938,6 @@ class Graded(GradedModulesCategory): sage: LieConformalAlgebras(QQbar).Graded() Category of H-graded Lie conformal algebras over Algebraic Field """ - def _repr_object_names(self): """ The names of the objects of this category @@ -691,31 +950,19 @@ def _repr_object_names(self): return "H-graded {}".format(self.base_category().\ _repr_object_names()) - class Super(SuperModulesCategory): - """ - The subcategory of super H-graded Lie conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(QQbar) - sage: C.Graded().Super() - Category of super H-graded Lie conformal algebras over Algebraic Field - sage: C.Graded().Super() is C.Super().Graded() - True - """ - def extra_super_categories(self): + class SubcategoryMethods: + def Super(self, base_ring=None): """ - The extra super categories of this category. + The category of H-graded super Lie conformal algebras. EXAMPLES:: - sage: C = LieConformalAlgebras(QQ).Graded().Super() - sage: C.super_categories() - [Category of super Lie conformal algebras over Rational Field, - Category of H-graded Lie conformal algebras over Rational Field] + sage: LieConformalAlgebras(QQ).Super().Graded() + Category of H-graded super Lie conformal algebras over Rational Field """ - return [self.base_category(),] - + assert base_ring is None or base_ring is self.base_ring() + return SuperModulesCategory.category_of( + self.base_category()).Graded() class ElementMethods: @abstract_method def degree(self): diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index d51e254e1c9..98d0907f8b4 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -66,18 +66,76 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).WithBasis().Super() Category of super Lie conformal algebras with basis over Algebraic Real Field """ - def extra_super_categories(self): + class ParentMethods: + + def _even_odd_on_basis(self, m): + """ + Return the parity of the basis element indexed by ``m``. + + OUTPUT: + + ``0`` if ``m`` is for an even element or ``1`` if ``m`` + is for an odd element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: B = V._indices + sage: V._even_odd_on_basis(B(('G',1))) + 1 + """ + return self._parity[self.monomial((m[0],0))] + + class ElementMethods: + + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for " + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + + class Graded(GradedModulesCategory): """ - The extra super categories of this category. + The category of H-graded super Lie conformal algebras with basis. EXAMPLES:: - sage: LieConformalAlgebras(QQ).WithBasis().Super().super_categories() - [Category of super modules with basis over Rational Field, - Category of Lie conformal algebras with basis over Rational Field, - Category of super Lie conformal algebras over Rational Field] + sage: C = LieConformalAlgebras(QQbar).WithBasis() + sage: C.Super().Graded() + Category of H-graded super Lie conformal algebras with basis over Algebraic Field + sage: C.Super().Graded() is C.Graded().Super() + True """ - return [self.base_category(),] + def _repr_object_names(self): + """ + The names of the objects of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().Super().Graded() + Category of H-graded super Lie conformal algebras with basis over Algebraic Field + """ + return "H-graded {}".format(self.base_category()._repr_object_names()) class Graded(GradedModulesCategory): """ @@ -99,32 +157,6 @@ def _repr_object_names(self): """ return "H-graded {}".format(self.base_category()._repr_object_names()) - class Super(SuperModulesCategory): - """ - The category of super H-graded Lie conformal algebras - with basis. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(QQbar).WithBasis() - sage: C.Graded().Super() - Category of super H-graded Lie conformal algebras with basis over Algebraic Field - sage: C.Graded().Super() is C.Super().Graded() - True - """ - def extra_super_categories(self): - """ - The extra super categories of this category. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).WithBasis().Graded().Super().super_categories() - [Category of super Lie conformal algebras with basis over Rational Field, - Category of H-graded Lie conformal algebras with basis over Rational Field, - Category of super H-graded Lie conformal algebras over Rational Field] - """ - return [self.base_category(),] - class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): """ The category of finitely generated Lie conformal @@ -148,20 +180,47 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).WithBasis().FinitelyGenerated().Super() Category of super finitely generated Lie conformal algebras with basis over Algebraic Real Field """ - def extra_super_categories(self): + class Graded(GradedModulesCategory): """ - The extra super categories of this category + The category of H-graded finitely generated super + Lie conformal algebras with basis. EXAMPLES:: - sage: C = LieConformalAlgebras(QQ).WithBasis().FinitelyGenerated().Super() - sage: C.super_categories() - [Category of super Lie conformal algebras with basis over Rational Field, - Category of finitely generated Lie conformal algebras with basis over Rational Field, - Category of super finitely generated Lie conformal algebras over Rational Field] + sage: C = LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() + sage: C.Graded().Super() + Category of H-graded super finitely generated Lie conformal algebras with basis over Algebraic Field + sage: C.Graded().Super() is C.Super().Graded() + True """ - return [self.base_category(),] + def _repr_object_names(self): + """ + The names of the objects of this category + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Super().Graded() + Category of H-graded super finitely generated Lie conformal algebras with basis over Algebraic Field + """ + return "H-graded {}".format(self.base_category().\ + _repr_object_names()) + + class ParentMethods: + + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.degree_on_basis(('L',2)) + 4 + """ + if m[0] in self._central_elements: + return 0 + return self._weights[self._index_to_pos[m[0]]] + m[1] class Graded(GradedModulesCategory): """ @@ -185,31 +244,20 @@ def _repr_object_names(self): return "H-graded {}".format(self.base_category().\ _repr_object_names()) - class Super(SuperModulesCategory): - """ - The category of super H-graded finitely generated - Lie conformal algebras with basis. - - EXAMPLES:: + class ParentMethods: - sage: C = LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() - sage: C.Graded().Super() - Category of super H-graded finitely generated Lie conformal algebras with basis over Algebraic Field - sage: C.Graded().Super() is C.Super().Graded() - True - """ - def extra_super_categories(self): - """ - The extra super categories of this category. + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. EXAMPLES:: - sage: C = LieConformalAlgebras(QQ).FinitelyGenerated().Graded().Super() - sage: C.super_categories() - [Category of super finitely generated Lie conformal algebras over Rational Field, - Category of super H-graded Lie conformal algebras over Rational Field, - Category of H-graded finitely generated Lie conformal algebras over Rational Field] + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.degree_on_basis(('L',2)) + 4 """ - return [self.base_category(),] - + if m[0] in self._central_elements: + return 0 + return self._weights[self._index_to_pos[m[0]]] + m[1] From 8cab080a573d93ea24cde106d1887487a756b84c Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 8 Jul 2020 17:06:35 -0400 Subject: [PATCH 051/379] Finished number field functionality, minimal examples --- src/sage/schemes/berkovich/berkovich_space.py | 126 +++++++++++------- 1 file changed, 76 insertions(+), 50 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 999a646ced9..2469481d70e 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -80,7 +80,7 @@ class Berkovich_Element(Element): class Berkovich_Element_Cp(Berkovich_Element): r""" - The abstract parent class for any element of Berkovich space over `\CC_p`. + The abstract parent class for any element of Berkovich space over `\CC_p`. This class should never be instantiated, instead Berkovich_Element_Cp_Affine or Berkovich_Element_Cp_Projective should be used. """ @@ -226,7 +226,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, #check containment for the sequence of disks previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] - dist = (center[0] - previous_center[0]).abs() + dist = self._custom_abs(center[0] - previous_center[0]) if previous_radius < radius or dist > radius: raise ValueError("sequence of disks does not define a Type IV point as" + \ "containment is not proper") @@ -274,7 +274,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, #check containment for the sequence of disks previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] - dist = (center - previous_center).abs() + dist = self._custom_abs(center - previous_center) if previous_radius < radius or dist > radius: raise ValueError("sequence of disks does not define a Type IV point as " + \ "containment is not proper") @@ -433,8 +433,11 @@ def _custom_abs(self, x): Returns the absolute value of ``x`` with respect to the norm on ``Cp``. Used to simplify code, as ``x`` may be a point of a number field - or a padic field. + or a padic field. """ + if self._base_type == 'padic': + return x.abs() + return (self.prime())**(self._valuation)(x) def precision(self): """ @@ -570,7 +573,7 @@ def path_distance_metric(self, other): Also referred to as the hyperbolic metric, or the big metric. On the set of Type II, III and IV points, the path distance metric - is a metric. Following Baker and Rumely, we extend + is a metric. Following Baker and Rumely, we extend the path distance metric to Type I points x,y by p(x,x) = 0 and p(x,y) = infinity. @@ -737,7 +740,7 @@ def prime(self): 3 """ return self._p - + def __ne__(self, other): """ Non-equality operator. @@ -818,18 +821,18 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): - Type III points are represented by a center in `\QQ_p` and a radius in `[0,\infty)` - - Type IV points are represented by a finite list of centers in `\QQ_p` and - a finite list of radii in `[0,\infty)`. + - Type IV points are represented by a finite list of centers in `\QQ_p` and + a finite list of radii in `[0,\infty)`. INPUT: - ``center`` -- For Type I, II, and III points, the center of the corresponding disk in `\CC_p`. Must be an element of `\QQ_p`, a finite extension of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers - used to approximate the point or a univariate function that computes the centers + used to approximate the point or a univariate function that computes the centers (computation starts at 1). - - ``radius`` -- (optional) For Type I, II, and III points, the radius of the + - ``radius`` -- (optional) For Type I, II, and III points, the radius of the corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, can be a list of radii used to approximate the point or a univariate function that computes the radii (computation starts at 1). @@ -840,8 +843,8 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point - ``error_check`` -- (default: True) If error checking should be run on input. If - input is correctly formatted, can be set to ``False`` for better performance. - WARNING: with error check set to ``False``, any error in the input will lead to + input is correctly formatted, can be set to ``False`` for better performance. + WARNING: with error check set to ``False``, any error in the input will lead to incorrect results. EXAMPLES: @@ -866,7 +869,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): Type II point centered at 2 + O(3^20) of radius 3^1/2 sage: c = B(2,1.6); c Type III point centered at 2 + O(3^20) of radius 1.60000000000000 - + Some Type II points may be mistaken for Type III points:: sage: b = B(3, 3**0.5); b @@ -876,7 +879,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: b = B(3, power=RR(1/100000)); b Type II point centered at 3 + O(3^21) of radius 3^1/100000 - + Type IV points can be constructed in a number of ways, the first being from a list of centers and radii used to approximate the point:: @@ -900,9 +903,23 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): to ``False``, any error in the input will lead to incorrect results:: sage: d = B(f,g,prec=100,error_check=False); d - Type IV point of precision 100 with centers given by + Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y - + + When creating a Berkovich space backed by a number field, points can be created similarily:: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: v = A.valuation(3) + sage: B = Berkovich_Cp_Projective(A,v) + sage: Q1 = B(a); Q1 + Type I point centered at (a : 1) + + :: + + sage: Q2 = B(a+1, 3); Q2 + Type II point centered at (a + 1 : 1) of radius 3^1 + TESTS:: sage: A= Berkovich_Cp_Affine(Qp(3)) @@ -910,8 +927,8 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): Type II point centered at 3 + O(3^21) of radius 3^0 sage: Q2=A(2.5,1); Q2 - Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + - 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + + Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20) of radius 3^0 sage: Q5=A(3,0); Q5 @@ -1045,7 +1062,7 @@ def __eq__(self, other): elif stype in [2,3] and otype in [2,3]: if self.radius() != other.radius(): return False - center_dist = (self.center() - other.center()).abs() + center_dist = self._custom_abs(self.center() - other.center()) return center_dist <= self.radius() else: return False @@ -1071,11 +1088,11 @@ def partial_order(self,other): The standard partial order on Berkovich space Roughly, the partial order corresponds to containment of - the corresponding disks in ``Cp``. + the corresponding disks in ``Cp``. - For example, let x and y be points of Type II or III. - If x has center ``c1`` and radius ``r1`` and y has center - ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) + For example, let x and y be points of Type II or III. + If x has center ``c1`` and radius ``r1`` and y has center + ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) is a subset of D(c2,r2) in ``Cp``. INPUT: @@ -1137,7 +1154,7 @@ def join(self, other, basepoint="infty"): INPUT: - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- (default: Infinity) A point of the same + - ``basepoint`` -- (default: Infinity) A point of the same Berkovich space as this point or the string 'infty' OUTPUT: A point of the same Berkovich space @@ -1209,7 +1226,7 @@ def involution_map(self): The involution map is the extension of the map ``z |-> 1/z`` map on ``Cp`` to Berkovich space. - For Affine Berkovich Space, not defined for the Type I + For Affine Berkovich Space, not defined for the Type I point centered at 0. OUTPUT: A point of the same Berkovich space @@ -1254,7 +1271,7 @@ def involution_map(self): power = self.power() return parent(0,power=-power) return parent(0,RR(1/radius)) - return parent(1/center,RR(radius/(center.abs()**2))) + return parent(1/center,RR(radius/(self._custom_abs(center)**2))) new_center_lst = [] new_radius_lst = [] @@ -1266,7 +1283,7 @@ def involution_map(self): new_radius = RR(1/radius[i]) else: new_center = 1/center[i] - new_radius = RR(radius[i]/(center[i].abs()**2)) + new_radius = RR(radius[i]/(self._custom_abs(center[i])**2)) new_center_lst.append(new_center) new_radius_lst.append(new_radius) return parent(new_center_lst,new_radius_lst,error_check=False) @@ -1354,7 +1371,7 @@ def spherical_kernel(self,other): r""" The spherical kernel of this point with ``other``. - The spherical kernel is one possible extension of + The spherical kernel is one possible extension of the spherical distance on `A^1(\CC_p)` to the Berkovich Affine line. @@ -1427,12 +1444,12 @@ def Hsia_kernel(self, other, basepoint): return "oo" return (self.spherical_kernel(other))/ \ (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) - + def diameter(self, basepoint="oo"): """ Generalized diameter function. - If the basepoint is infinity, the diameter is equal to + If the basepoint is infinity, the diameter is equal to the limit of the radii of the corresponding disks in ``Cp``. If the basepoint is not infinity, the diameter @@ -1441,7 +1458,7 @@ def diameter(self, basepoint="oo"): INPUT: - - ``basepoint`` -- (default = Infinity) A point of the + - ``basepoint`` -- (default = Infinity) A point of the same Berkovich space as this point OUTPUT: A real number or Infinity @@ -1487,16 +1504,16 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): INPUT: - - ``center`` -- For Type I, II, and III points, the center of the + - ``center`` -- For Type I, II, and III points, the center of the corresponding disk in `P^1(\CC_p)`. Must be an element of `\QQ_p`, a finite extension of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers - used to approximate the point or a univariate function that computes the centers + used to approximate the point or a univariate function that computes the centers (computation starts at 1). - ``radius`` -- (optional) For Type I, II, and III points, the radius of the corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, can be a list of radii used to approximate the point or a univariate function that - computes the radii (computation starts at 1). + computes the radii (computation starts at 1). - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies the power of ``p`` such that p^ ``power`` = radius. @@ -1545,7 +1562,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): 5^10 + 3*5^11 + 5^12 + 3*5^13 + 5^14 + 3*5^15 + 5^16 + 3*5^17 + 5^18 + 3*5^19 + O(5^20) : 1 + O(5^20))] ... with radii [1.76100000000000, 1.12300000000000] ... - Type IV points can also be created from univariate functions. Since the centers of + Type IV points can also be created from univariate functions. Since the centers of the sequence of disks can not be the point at infinity in `P^1(\CC_p)`, only functions into `\CC_p` are supported:: @@ -1658,7 +1675,7 @@ def __eq__(self, other): return False scent = self.center()[0] ocent = other.center()[0] - center_dist = (scent - ocent).abs() + center_dist = self._custom_abs(scent - ocent) return center_dist <= self.radius() else: return False @@ -1670,7 +1687,7 @@ def __hash__(self): EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: P = B.base_ring() + sage: P = ProjectiveSpace(B.base_ring(),1) sage: Q1 = B(P.point([2,2],False), RR(3**(1/2))) sage: Q2 = B([1,1], 3**(1/2)) sage: hash(Q1) == hash(Q2) @@ -1760,18 +1777,18 @@ def partial_order(self,other): return not s_less_than_o else: if other.type_of_point() == 1: - dist = (self.center()[0] - other.center()[0]).abs() + dist = self._custom_abs(self.center()[0] - other.center()[0]) if dist <= self.radius(): return True return None if other.type_of_point() == 4: center = other.center()[len(other.center())] - dist = (self.center()[0] - center[0]).abs() + dist = self._custom_abs(self.center()[0] - center[0]) if dist <= self.radius(): return True return None else: - dist = (self.center()[0]-other.center()[0]).abs() + dist = self._custom_abs(self.center()[0]-other.center()[0]) if(dist <= self.radius()): if(other.radius() <= self.radius()): return True @@ -1856,7 +1873,7 @@ def join(self, other, basepoint="infty"): if basepoint == "infty" or basepoint == infty: if self == infty or other == infty: return infty - dist = (self.center()[0] - other.center()[0]).abs() + dist = self._custom_abs(self.center()[0] - other.center()[0]) return parent(self.center(),max(dist,self.radius(),other.radius())) #TODO optimize for Type II if not isinstance(basepoint, Berkovich_Element_Cp_Projective): @@ -1889,8 +1906,8 @@ def join(self, other, basepoint="infty"): if b_greater_than_o == None: if b_greater_than_s == None: #case where none of the points are comparable - dist_b_s = (self.center()[0] - basepoint.center()[0]).abs() - dist_b_o = (other.center()[0] - basepoint.center()[0]).abs() + dist_b_s = self._custom_abs(self.center()[0] - basepoint.center()[0]) + dist_b_o = self._custom_abs(other.center()[0] - basepoint.center()[0]) return parent(basepoint.center(),\ min(max(dist_b_o,other.radius(),basepoint.radius()),\ max(dist_b_s,self.radius(),basepoint.radius()))) @@ -1978,7 +1995,7 @@ def involution_map(self): power = self.power() return parent(0,power=-power) return parent(0,1/radius) - return parent(1/center[0],radius/(center[0].abs()**2)) + return parent(1/center[0],radius/(self._custom_abs(center[0])**2)) new_center_lst = [] new_radius_lst = [] @@ -1990,7 +2007,7 @@ def involution_map(self): new_radius = 1/radius[i] else: new_center = 1/center[i][0] - new_radius = radius[i]/(center[i][0].abs()**2) + new_radius = radius[i]/(self._custom_abs(center[i][0])**2) new_center_lst.append(new_center) new_radius_lst.append(new_radius) return parent(new_center_lst,new_radius_lst) @@ -2413,7 +2430,18 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Type I point centered at (2 + O(3) : 1 + O(3)) Note that this point has very low precision, as S has low - precision cap. + precision cap. Berkovich space can also be created over + a number field, as long as a valuation is specified:: + + sage: R. = QQ[] + sage: A. = NumberField(x^2+1) + sage: v = A.valuation(a+1) + sage: B = Berkovich_Cp_Projective(A,v); B + Projective Berkovich line over Cp(2), with base + Number Field in a with defining polynomial x^2 + 1 + + Number fields have the benefit that computation is exact, + but lack support for all of `\CC_p`. """ Element = Berkovich_Element_Cp_Projective @@ -2495,15 +2523,13 @@ def base_ring(self): sage: B = Berkovich_Cp_Projective(3) sage: B.base_ring() - Projective Space of dimension 1 over 3-adic Field with - capped relative precision 20 + 3-adic Field with capped relative precision 20 :: sage: C = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3,1),1)) sage: C.base_ring() - Projective Space of dimension 1 over 3-adic Field with - capped relative precision 1 + 3-adic Field with capped relative precision 1 """ return self.base().base_ring() From 347e2865ffbddc95f9c9d05df7495b356ae38a11 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 9 Jul 2020 10:57:49 +0200 Subject: [PATCH 052/379] remove known bug tags for fixed bugs --- src/sage/coding/grs_code.py | 2 +- src/sage/coding/punctured_code.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 0061eeda509..362f46627a8 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -1920,7 +1920,7 @@ def decode_to_message(self, word_and_erasure_vector): sage: n_era = randint(0, C.minimum_distance() - 2) sage: Chan = channels.ErrorErasureChannel(C.ambient_space(), D.decoding_radius(n_era), n_era) sage: y = Chan(c) - sage: D.connected_encoder().unencode(c) == D.decode_to_message(y) # known bug + sage: D.connected_encoder().unencode(c) == D.decode_to_message(y) True TESTS: diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index 33d3e7fac6f..36a881d6d0a 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -625,7 +625,7 @@ def decode_to_code(self, y): sage: y = Chan(c) sage: y in Cp False - sage: D.decode_to_code(y) == c # known bug + sage: D.decode_to_code(y) == c True """ D = self.original_decoder() From d00112e008270a70a743fe8dc4216bce86d58840 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Thu, 9 Jul 2020 12:58:44 -0300 Subject: [PATCH 053/379] Moved methods to implementation classes Every method that needs to be repeated for LCAs and Super LCAs is now in some implementation class. Added a new abstract class for finitely generated Lie conformal algebras. --- .../algebras/lie_conformal_algebras.rst | 1 + .../finitely_freely_generated_lca.py | 3 +- ...initely_generated_lie_conformal_algebra.py | 75 +++++ .../graded_lie_conformal_algebra.py | 15 + .../lie_conformal_algebra.py | 20 ++ .../lie_conformal_algebra_element.py | 184 ++++++++++- .../lie_conformal_algebra_with_basis.py | 3 +- ...nitely_generated_lie_conformal_algebras.py | 98 ------ src/sage/categories/lie_conformal_algebras.py | 285 ------------------ .../lie_conformal_algebras_with_basis.py | 91 ------ 10 files changed, 293 insertions(+), 482 deletions(-) create mode 100644 src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py diff --git a/src/doc/en/reference/algebras/lie_conformal_algebras.rst b/src/doc/en/reference/algebras/lie_conformal_algebras.rst index 0752d5843b7..a20090e29ea 100644 --- a/src/doc/en/reference/algebras/lie_conformal_algebras.rst +++ b/src/doc/en/reference/algebras/lie_conformal_algebras.rst @@ -30,6 +30,7 @@ See also .. toctree:: ../sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca + ../sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py index b204c4a89b7..0b14198d210 100644 --- a/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +++ b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py @@ -20,8 +20,9 @@ from sage.categories.lie_conformal_algebras import LieConformalAlgebras from .freely_generated_lie_conformal_algebra import \ FreelyGeneratedLieConformalAlgebra +from .finitely_generated_lie_conformal_algebra import FinitelyGeneratedLieConformalAlgebra -class FinitelyFreelyGeneratedLCA(FreelyGeneratedLieConformalAlgebra): +class FinitelyFreelyGeneratedLCA(FreelyGeneratedLieConformalAlgebra, FinitelyGeneratedLieConformalAlgebra): """ Abstract base class for finitely generated Lie conformal algebras. diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py new file mode 100644 index 00000000000..9084bda77e1 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py @@ -0,0 +1,75 @@ +r""" +Finitely Generated Lie Conformal Algebra + +AUTHORS: + +- Reimundo Heluani (2020-07-09): Initial implementation. +""" + + +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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 .lie_conformal_algebra import LieConformalAlgebra + +class FinitelyGeneratedLieConformalAlgebra(LieConformalAlgebra): + """ + Base class for finitely generated (super) Lie conformal algebras. + """ + def gen(self,i): + r""" + The ``i``-th generator of this Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: V.gen(0) + B[alpha[1]] + sage: V.1 + B[alphacheck[1]] + """ + return self.gens()[i] + + def some_elements(self): + """ + Some elements of this Lie conformal algebra. + + This method returns a list with elements containing at + least the generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.some_elements() + [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + + def ngens(self): + r""" + The number of generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ngens() + 2 + + sage: V = lie_conformal_algebras.Affine(QQ, 'A2') + sage: V.ngens() + 9 + """ + return len(self.gens()) diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py index 9f4500d28b4..8d9f153fcb5 100644 --- a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -130,3 +130,18 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, raise ValueError("weights and (non-central) generator lists "\ "must be of same length") self._weights = weights + + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.degree_on_basis(('L',2)) + 4 + """ + if m[0] in self._central_elements: + return 0 + return self._weights[self._index_to_pos[m[0]]] + m[1] diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py index 0ec6ca765c3..3a511ed1a7b 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -349,3 +349,23 @@ def __classcall_private__(cls, R=None, arg0=None, index_set=None, prefix=prefix, names=names, latex_names=latex_names, parity=parity, **kwds) raise NotImplementedError("not implemented") + + + def ideal(self, *gens, **kwds): + r""" + The ideal of this Lie conformal algebra generated by ``gens``. + + .. TODO:: + + Ideals of Lie Conformal Algebras are not implemented yet. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ideal() + Traceback (most recent call last): + ... + NotImplementedError: ideals of Lie Conformal algebras are not implemented yet + """ + raise NotImplementedError("ideals of Lie Conformal algebras are " + "not implemented yet") diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index de30de070f6..3b21946436b 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -22,11 +22,184 @@ from sage.misc.misc import repr_lincomb from sage.misc.latex import latex from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement +from sage.structure.element import Element +from sage.misc.abstract_method import abstract_method +from sage.structure.element import coerce_binop -class LCAWithGeneratorsElement(IndexedFreeModuleElement): +class LieConformalAlgebraElement(Element): """ - The element class of a Lie conformal algebra with a - preferred set of generators. + Base class for elements of (super) Lie conformal algebras. + """ + @coerce_binop + def bracket(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal algebra:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L.bracket(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H.bracket(H) + {1: 2*B['K']} + sage: E.bracket(F) + {0: B[alphacheck[1]], 1: B['K']} + """ + return self._bracket_(rhs) + + @abstract_method + def _bracket_(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal Algebra:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L._bracket_(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L._bracket_(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H._bracket_(H) + {1: 2*B['K']} + sage: E._bracket_(F) + {0: B[alphacheck[1]], 1: B['K']} + """ + + @coerce_binop + def nproduct(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.nproduct(L, 3) + 1/2*C + sage: L.nproduct(L.T(), 0) + 2*T^(2)L + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E.nproduct(H, 0) == - 2*E + True + sage: E.nproduct(F, 1) + B['K'] + """ + return self._nproduct_(rhs,n) + + def _nproduct_(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L._nproduct_(L,3) + 1/2*C + sage: L._nproduct_(L.T(),0) + 2*T^(2)L + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E._nproduct_(H,0) == - 2*E + True + sage: E._nproduct_(F,1) + B['K'] + """ + if n >= 0: + return self.bracket(rhs).get(n,self.parent().zero()) + else: + raise NotImplementedError("vertex algebras are not implemented") + + @abstract_method + def T(self, n=1): + r""" + The ``n``-th derivative of ``self``. + + INPUT: + + - ``n`` -- integer (default:``1``); how many times + to apply `T` to this element + + OUTPUT: + + `T^n a` where `a` is this element. Notice that we use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.inject_variables() + Defining L, C + sage: L.T() + TL + sage: L.T(3) + 6*T^(3)L + sage: C.T() + 0 + """ + +class LCAWithBasisElement(IndexedFreeModuleElement, LieConformalAlgebraElement): + """ + Element class of a (super) Lie conformal algebra with basis. + """ + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for " + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + +class FreelyGeneratedLCAElement(LCAWithBasisElement): + """ + Element class of a freely generated (super) Lie conformal algebra. """ def T(self,n=1): r""" @@ -88,10 +261,9 @@ def is_monomial(self): """ return len(self._monomial_coefficients) == 1 or self.is_zero() - -class LCAStructureCoefficientsElement(LCAWithGeneratorsElement): +class LCAStructureCoefficientsElement(FreelyGeneratedLCAElement): """ - An element of a Lie conformal algebra given by structure + Element class of a (super) Lie conformal algebra given by structure coefficients. """ def _bracket_(self, right): diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py index e56785601fe..07863f06857 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py @@ -18,8 +18,9 @@ from sage.categories.lie_conformal_algebras import LieConformalAlgebras from sage.combinat.free_module import CombinatorialFreeModule +from .lie_conformal_algebra import LieConformalAlgebra -class LieConformalAlgebraWithBasis(CombinatorialFreeModule): +class LieConformalAlgebraWithBasis(CombinatorialFreeModule, LieConformalAlgebra): """ Abstract base class for a Lie conformal algebra with a preferred basis. diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 90a38209b02..f8280e67fa7 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -29,58 +29,6 @@ class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): sage: LieConformalAlgebras(QQbar).FinitelyGenerated() Category of finitely generated Lie conformal algebras over Algebraic Field """ - class ParentMethods: - def ngens(self): - r""" - The number of generators of this Lie conformal algebra. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ngens() - 2 - - sage: V = lie_conformal_algebras.Affine(QQ, 'A2') - sage: V.ngens() - 9 - """ - return len(self.gens()) - - def gen(self,i): - r""" - The ``i``-th generator of this Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: V.gen(0) - B[alpha[1]] - sage: V.1 - B[alphacheck[1]] - """ - return self.gens()[i] - - def some_elements(self): - """ - Some elements of this Lie conformal algebra. - - This method returns a list with elements containing at - least the generators. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) - sage: V.some_elements() - [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] - """ - S = list(self.gens()) - from sage.misc.misc import some_tuples - for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): - S.append(x.T() + 2*y.T(2)) - return S - class Super(SuperModulesCategory): """ The category of super finitely generated Lie conformal algebras. @@ -90,52 +38,6 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).FinitelyGenerated().Super() Category of super finitely generated Lie conformal algebras over Algebraic Real Field """ - class ParentMethods: - def ngens(self): - r""" - The number of generators of this super Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.ngens() - 3 - """ - return len(self.gens()) - - def gen(self,i): - r""" - The ``i``-th generator of this super Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.gens() - (L, G, C) - sage: V.gen(0) - L - """ - return self.gens()[i] - - def some_elements(self): - """ - Some elements of this super Lie conformal algebra. - - This method returns a list with elements containing at - least the generators. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.some_elements() - [L, G, C, TG + 4*T^(2)L, TG + 4*T^(2)G, TL + 4*T^(2)L] - """ - S = list(self.gens()) - from sage.misc.misc import some_tuples - for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): - S.append(x.T() + 2*y.T(2)) - return S - class Graded(GradedModulesCategory): """ The category of H-graded super finitely generated Lie conformal algebras. diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index ec4d4eb24d7..bedd04145aa 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -131,7 +131,6 @@ from .category_types import Category_over_base_ring from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method -from sage.structure.element import coerce_binop from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory from sage.categories.commutative_rings import CommutativeRings @@ -268,25 +267,6 @@ def _repr_object_names(self): class ParentMethods: - def ideal(self, *gens, **kwds): - r""" - The ideal of this Lie conformal algebra generated by ``gens``. - - .. TODO:: - - Ideals of Lie Conformal Algebras are not implemented yet. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ideal() - Traceback (most recent call last): - ... - NotImplementedError: ideals of Lie Conformal algebras are not implemented yet - """ - raise NotImplementedError("ideals of Lie Conformal algebras are " - "not implemented yet") - def is_super(self): """ Wether this Lie conformal algebra is a super Lie @@ -419,142 +399,6 @@ def _test_jacobi(self, **options): tester.assertDictEqual(jacobiator, {}) class ElementMethods: - @coerce_binop - def bracket(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - EXAMPLES: - - The brackets of the Virasoro Lie conformal algebra:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.bracket(L) - {0: TL, 1: 2*L, 3: 1/2*C} - sage: L.bracket(L.T()) - {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} - - Now with a current algebra:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: E = V.0; H = V.1; F = V.2; - sage: H.bracket(H) - {1: 2*B['K']} - sage: E.bracket(F) - {0: B[alphacheck[1]], 1: B['K']} - """ - return self._bracket_(rhs) - - @abstract_method - def _bracket_(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES: - - The brackets of the Virasoro Lie conformal Algebra:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L._bracket_(L) - {0: TL, 1: 2*L, 3: 1/2*C} - sage: L._bracket_(L.T()) - {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} - - Now with a current algebra:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: E = V.0; H = V.1; F = V.2; - sage: H._bracket_(H) - {1: 2*B['K']} - sage: E._bracket_(F) - {0: B[alphacheck[1]], 1: B['K']} - """ - - @coerce_binop - def nproduct(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.nproduct(L, 3) - 1/2*C - sage: L.nproduct(L.T(), 0) - 2*T^(2)L - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: E = V.0; H = V.1; F = V.2; - sage: E.nproduct(H, 0) == - 2*E - True - sage: E.nproduct(F, 1) - B['K'] - """ - return self._nproduct_(rhs,n) - - def _nproduct_(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L._nproduct_(L,3) - 1/2*C - sage: L._nproduct_(L.T(),0) - 2*T^(2)L - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: E = V.0; H = V.1; F = V.2; - sage: E._nproduct_(H,0) == - 2*E - True - sage: E._nproduct_(F,1) - B['K'] - """ - if n >= 0: - return self.bracket(rhs).get(n,self.parent().zero()) - else: - raise NotImplementedError("vertex algebras are not implemented") - - @abstract_method - def T(self, n=1): - r""" - The ``n``-th derivative of ``self``. - - INPUT: - - - ``n`` -- integer (default:``1``); how many times - to apply `T` to this element - - OUTPUT: - - `T^n a` where `a` is this element. Notice that we use the - *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.inject_variables() - Defining L, C - sage: L.T() - TL - sage: L.T(3) - 6*T^(3)L - sage: C.T() - 0 - """ def is_even_odd(self): """ @@ -627,27 +471,6 @@ def example(self): class ParentMethods: - def ideal(self, *gens, **kwds): - r""" - The ideal of this super Lie conformal algebra generated - by ``gens``. - - .. TODO:: - - Ideals of super Lie Conformal Algebras are not - implemented yet. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.ideal() - Traceback (most recent call last): - ... - NotImplementedError: ideals of super Lie Conformal algebras are not implemented yet - """ - raise NotImplementedError("ideals of super Lie Conformal algebras " - "are not implemented yet") - def is_super(self): """ This method returns ``True``. @@ -795,100 +618,6 @@ def _test_jacobi(self, **options): tester.assertDictEqual(jacobiator, {}) class ElementMethods: - @coerce_binop - def bracket(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - EXAMPLES: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.bracket(G.T()) - {0: 2*TL, 1: 2*L, 3: 2*C} - """ - return self._bracket_(rhs) - - @abstract_method - def _bracket_(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G._bracket_(G.T()) - {0: 2*TL, 1: 2*L, 3: 2*C} - """ - - @coerce_binop - def nproduct(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.nproduct(G.T(), 1) - 2*L - """ - return self._nproduct_(rhs, n) - - def _nproduct_(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.nproduct(G.T(), 1) - 2*L - """ - if n >= 0: - return self.bracket(rhs).get(n,self.parent().zero()) - else: - raise NotImplementedError("super vertex algebras are not implemented") - - @abstract_method - def T(self, n=1): - r""" - The ``n``-th derivative of ``self``. - - INPUT: - - - ``n`` -- integer (default:``1``); how many times - to apply `T` to this element - - OUTPUT: - - `T^n a` where `a` is this element. Notice that we use the - *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3) - 6*T^(3)G - """ @abstract_method def is_even_odd(self): @@ -963,20 +692,6 @@ def Super(self, base_ring=None): assert base_ring is None or base_ring is self.base_ring() return SuperModulesCategory.category_of( self.base_category()).Graded() - class ElementMethods: - @abstract_method - def degree(self): - """ - The degree of this element. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.degree() - 2 - sage: L.T(3).degree() - 5 - """ WithBasis = LazyImport('sage.categories.lie_conformal_algebras_with_basis', 'LieConformalAlgebrasWithBasis') diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index 98d0907f8b4..5894227af41 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -29,34 +29,6 @@ class LieConformalAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): sage: LieConformalAlgebras(QQbar).WithBasis() Category of Lie conformal algebras with basis over Algebraic Field """ - class ElementMethods: - - def index(self): - """ - The index of this basis element. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3).index() - ('G', 3) - sage: v = V.an_element(); v - L + G + C - sage: v.index() - Traceback (most recent call last): - ... - ValueError: index can only be computed for monomials, got L + G + C - """ - if self.is_zero(): - return None - if not self.is_monomial(): - raise ValueError ("index can only be computed for " - "monomials, got {}".format(self)) - - return next(iter(self.monomial_coefficients())) - class Super(SuperModulesCategory): """ The category of super Lie conformal algebras with basis. @@ -86,34 +58,6 @@ def _even_odd_on_basis(self, m): """ return self._parity[self.monomial((m[0],0))] - class ElementMethods: - - def index(self): - """ - The index of this basis element. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3).index() - ('G', 3) - sage: v = V.an_element(); v - L + G + C - sage: v.index() - Traceback (most recent call last): - ... - ValueError: index can only be computed for monomials, got L + G + C - """ - if self.is_zero(): - return None - if not self.is_monomial(): - raise ValueError ("index can only be computed for " - "monomials, got {}".format(self)) - - return next(iter(self.monomial_coefficients())) - class Graded(GradedModulesCategory): """ The category of H-graded super Lie conformal algebras with basis. @@ -205,23 +149,6 @@ def _repr_object_names(self): return "H-graded {}".format(self.base_category().\ _repr_object_names()) - class ParentMethods: - - def degree_on_basis(self, m): - r""" - Return the degree of the basis element indexed by ``m`` - in ``self``. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.degree_on_basis(('L',2)) - 4 - """ - if m[0] in self._central_elements: - return 0 - return self._weights[self._index_to_pos[m[0]]] + m[1] - class Graded(GradedModulesCategory): """ The category of H-graded finitely generated Lie conformal @@ -243,21 +170,3 @@ def _repr_object_names(self): """ return "H-graded {}".format(self.base_category().\ _repr_object_names()) - - class ParentMethods: - - def degree_on_basis(self, m): - r""" - Return the degree of the basis element indexed by ``m`` - in ``self``. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.degree_on_basis(('L',2)) - 4 - """ - if m[0] in self._central_elements: - return 0 - return self._weights[self._index_to_pos[m[0]]] + m[1] - From 87189ecf82fdec6cb077816da0ab30d7ab9f3ae9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 15 Jul 2020 10:08:51 -0700 Subject: [PATCH 054/379] build/pkgs/cmake: Upgrade to 3.17.3 --- build/pkgs/cmake/checksums.ini | 7 +-- build/pkgs/cmake/package-version.txt | 2 +- build/pkgs/cmake/patches/osx10.10.patch | 57 ------------------------- 3 files changed, 5 insertions(+), 61 deletions(-) delete mode 100644 build/pkgs/cmake/patches/osx10.10.patch diff --git a/build/pkgs/cmake/checksums.ini b/build/pkgs/cmake/checksums.ini index 2987b766788..5d281924e17 100644 --- a/build/pkgs/cmake/checksums.ini +++ b/build/pkgs/cmake/checksums.ini @@ -1,4 +1,5 @@ tarball=cmake-VERSION.tar.gz -sha1=7851f87185f80e260ea634d5985ec9215fe408c8 -md5=f3ebc79b5dec85b49abe75958ffa1a03 -cksum=1559829871 +sha1=faef76cfc9f07cae7eecee72eba0d28abbbac5b4 +md5=d47f23a9781b68014e77717f8e291bb7 +cksum=4044419587 +upstream_url=https://github.com/Kitware/CMake/releases/download/vVERSION/cmake-VERSION.tar.gz diff --git a/build/pkgs/cmake/package-version.txt b/build/pkgs/cmake/package-version.txt index afad818663d..56cc1b61f82 100644 --- a/build/pkgs/cmake/package-version.txt +++ b/build/pkgs/cmake/package-version.txt @@ -1 +1 @@ -3.11.0 +3.17.3 diff --git a/build/pkgs/cmake/patches/osx10.10.patch b/build/pkgs/cmake/patches/osx10.10.patch deleted file mode 100644 index 81099344a47..00000000000 --- a/build/pkgs/cmake/patches/osx10.10.patch +++ /dev/null @@ -1,57 +0,0 @@ -Patch CMake to avoid using faulty system header in OSX 10.10 - -CMake project files include CoreFoundation/CoreFoundation.h which is a collection -of headers one of which is CoreFoundation/CFStream.h. It includes faulty header -dispatch/dispatch.h. Since CoreFoundation/CFStream.h is not needed for CMake, -specific headers needed are included to replace CoreFoundation/CoreFoundation.h - -diff -dru src/Source/cmFindProgramCommand.cxx new/Source/cmFindProgramCommand.cxx ---- src/Source/cmFindProgramCommand.cxx 2017-05-02 14:59:43.000000000 +0200 -+++ new/Source/cmFindProgramCommand.cxx 2017-05-18 15:56:09.094011660 +0200 -@@ -9,7 +9,7 @@ - class cmExecutionStatus; - - #if defined(__APPLE__) --#include -+#include - #endif - - struct cmFindProgramHelper -diff -dru src/Source/cmXCodeObject.cxx new/Source/cmXCodeObject.cxx ---- src/Source/cmXCodeObject.cxx 2017-05-02 14:59:43.000000000 +0200 -+++ new/Source/cmXCodeObject.cxx 2017-05-18 15:57:35.222438748 +0200 -@@ -2,7 +2,7 @@ - file Copyright.txt or https://cmake.org/licensing for details. */ - #include "cmXCodeObject.h" - --#include -+#include - #include - - #include "cmSystemTools.h" -diff -dru src/Source/CPack/cmCPackDragNDropGenerator.cxx new/Source/CPack/cmCPackDragNDropGenerator.cxx ---- src/Source/CPack/cmCPackDragNDropGenerator.cxx 2017-05-02 14:59:43.000000000 +0200 -+++ new/Source/CPack/cmCPackDragNDropGenerator.cxx 2017-05-18 22:00:35.917646538 +0200 -@@ -12,8 +12,9 @@ - #include - #include - #include -+#include - --#include -+#include - - #ifdef HAVE_CoreServices - // For the old LocaleStringToLangAndRegionCodes() function, to convert -diff -dru src/Source/CPack/OSXScriptLauncher.cxx new/Source/CPack/OSXScriptLauncher.cxx ---- src/Source/CPack/OSXScriptLauncher.cxx 2017-05-02 14:59:43.000000000 +0200 -+++ new/Source/CPack/OSXScriptLauncher.cxx 2017-05-18 16:00:45.179380694 +0200 -@@ -8,7 +8,7 @@ - #include - #include - --#include -+#include - - // For the PATH_MAX constant - #include From d0a80ce6eda30ebe22e2b48eeb7a0465f022bf72 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 15 Jul 2020 14:23:30 -0400 Subject: [PATCH 055/379] 29844: finished number field documentation --- src/sage/schemes/berkovich/berkovich_space.py | 839 ++++++++++-------- 1 file changed, 471 insertions(+), 368 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 2469481d70e..f55d9e79ecd 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -46,6 +46,7 @@ from sage.schemes.generic.scheme import Scheme from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ +from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal from sage.rings.number_field.number_field import is_NumberField from sage.rings.infinity import Infinity @@ -91,14 +92,14 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, from sage.rings.fraction_field_element import FractionFieldElement_1poly_field self._type = None - #if radius is a list, this is a Type 4 point + #if radius is a list, this is a type 4 point if isinstance(radius, list): if error_check: if not isinstance(center, list): raise ValueError("center was passed a list but radius was not a list") if len(radius) != len(center): raise ValueError("the same number of centers and radii must be specified to create " + \ - "a Type IV point") + "a type IV point") self._center_lst = list(center) self._radius_lst = list(radius) self._prec = len(self._radius_lst) @@ -110,7 +111,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, #is_FunctionFieldElement calls .parent elif hasattr(center, "parent"): - #if center is a supported univariate function, this is a Type 4 point + #if center is a supported univariate function, this is a type 4 point if is_FunctionFieldElement(center) or is_Polynomial(center) or\ isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): #check that both center and radii are supported univariate function @@ -120,7 +121,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if is_Expression(center): if len(center.variables()) != 1: raise ValueError("an expression with %s " %(len(center.variables())) + \ - "variables cannot define the centers approximating a Type IV point") + "variables cannot define the centers approximating a type IV point") else: #we do this since .subs is currently bugged for polynomials but not expressions center_expr_check = True @@ -130,7 +131,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if is_Expression(radius): if len(radius.variables()) != 1: raise ValueError("an expression with %s " %(len(radius.variables())) + \ - "variables cannot define the radii approximating a Type IV point") + "variables cannot define the radii approximating a type IV point") else: radius_expr_check = True else: @@ -179,10 +180,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("the center of a point of Projective Berkovich" + \ + raise ValueError("the center of a point of projective Berkovich" + \ "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) else: - if self._base_type == 'padic': + if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(),pAdicBaseGeneric): try: @@ -191,7 +192,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("the center of a point of Projective Berkovich space must be a " + \ + raise ValueError("the center of a point of projective Berkovich space must be a " + \ "point of P^1(Cp) or coerce into %s") %self._base_space else: try: @@ -199,18 +200,17 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: pass if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("the center of a disk approximating a Type IV point of Berkovich space" + \ + raise ValueError("the center of a disk approximating a type IV point of Berkovich space" + \ " over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) else: - base_field = self.parent().base_ring() - if not (center[0] in base_field and center[1] in base_field): + if center not in self._base_space: raise ValueError('center of a point of Berkovich space in projective space ' + \ 'over the wrong field') if center.scheme().ambient_space() != center.scheme(): raise ValueError("the center of a point of Berkovich space over " + \ "P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) if center == (center.scheme())((1,0)): - raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ + raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ "space cannot be centered at %s" %((center.scheme())((1,0)))) #make sure the radius coerces into the reals if not is_RealNumber(radius): @@ -220,7 +220,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("the radius of a disk approximating a Type IV point" + \ + raise ValueError("the radius of a disk approximating a type IV point" + \ "must coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks @@ -228,14 +228,14 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, previous_radius = self._radius_lst[i-1] dist = self._custom_abs(center[0] - previous_center[0]) if previous_radius < radius or dist > radius: - raise ValueError("sequence of disks does not define a Type IV point as" + \ + raise ValueError("sequence of disks does not define a type IV point as" + \ "containment is not proper") return elif child == "affine": for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] - if self._base_type == 'padic': + if self._base_type == 'padic field': #make sure the center is in Cp if not isinstance(center, pAdicGenericElement): try: @@ -245,7 +245,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("the center of a disk approximating a Type IV point must " + \ + raise ValueError("the center of a disk approximating a type IV point must " + \ "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) elif not is_pAdicField(center.parent()): try: @@ -253,7 +253,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: pass if (center.parent()).prime() != self._p: - raise ValueError("the center of a disk approximating a Type IV point of Berkovich " + \ + raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ "space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) else: #make sure the center is in the appropriate number field @@ -268,7 +268,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("the radius of a disk approximating a Type IV point must " + \ + raise ValueError("the radius of a disk approximating a type IV point must " + \ "coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks @@ -276,7 +276,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, previous_radius = self._radius_lst[i-1] dist = self._custom_abs(center - previous_center) if previous_radius < radius or dist > radius: - raise ValueError("sequence of disks does not define a Type IV point as " + \ + raise ValueError("sequence of disks does not define a type IV point as " + \ "containment is not proper") return else: @@ -284,20 +284,20 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, "Berkovich_Element_Cp directly" ) return - #the point must now be Type 1, 2, or 3, so we check that the center is of the appropriate type + #the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type if error_check: - if child == "affine": - if self._base_type == 'padic': - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of Affine Berkovich space over " + \ - "%s must convert to %s" %(self._base_space,self._base_space)) - elif not is_pAdicField(center.parent()): + if child == 'affine': + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + flag = False + except: + flag = True + if flag: + raise ValueError("the center of a point of affine Berkovich space over " + \ + "%s must convert to %s" %(self._base_space,self._base_space)) + if self._base_type == 'padic field': + if not is_pAdicField(center.parent()): try: center = (self._base_space)(center) except: @@ -317,9 +317,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("the center of a point of Projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s" %self._base_space) - if self._base_type == 'padic': + if self._base_type == 'padic field': + raise ValueError("the center of a point of projective Berkovich space must be a " + \ + "point of P^1(Cp) or coerce into %s" %self._base_space) + else: + raise ValueError('the center of a point of projective Berkovich '+ \ + 'space over %s must be coerce into %s' %(self._base_space, self._base_space)) + if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: @@ -328,7 +332,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, except: flag = True if flag: - raise ValueError("the center of a point of Projective Berkovich space must be a " + \ + raise ValueError("the center of a point of projective Berkovich space must be a " + \ "point of P^1(Cp) or coerce into %s") %self._base_space else: try: @@ -339,22 +343,26 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("the center of a point of Berkovich space over " + \ "P^1(Cp(%s)) cannot be a point of %s" %(self._p, center.scheme())) else: - if not(center[0] in self._base_space.base_ring() and center[1] in self._base_space.base_ring()): - raise ValueError('center defined over %s ' %center.parent() + \ - 'but Berkovich space defined over %s' %self._base_space) + if not(center in self._base_space): + raise ValueError('center defined over %s passed to ' %center.parent()+ \ + 'Berkovich space defined over number field %s' %self._base_space) if not(center.scheme().ambient_space() is center.scheme()): - raise ValueError("the center of a point of Projective Berkovich space cannot be " + \ + raise ValueError("the center of a point of projective Berkovich space cannot be " + \ "a point of %s" %(center.scheme())) else: raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ - "Berkovich_Element_Cp directly" ) + "Berkovich_Element_Cp directly") + if self._base_type == 'number field': + if not(center in self._base_space): + raise ValueError('center defined over %s passed to ' %center.parent()+ \ + 'Berkovich space defined over number field %s' %self._base_space) self._center = center if (radius == None and power == None) or radius == 0: self._type = 1 self._radius = 0 self._power = None return - #In order to simplify our representation, Type II and III points cannot be centered at infinity + #In order to simplify our representation, type II and III points cannot be centered at infinity if child == "projective": try: center.dehomogenize(1) @@ -371,7 +379,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if flag: raise ValueError("power must convert to rationals") if radius != None: - if radius != self._p**power: + if radius != RR(self._p**power): raise ValueError("conflicting inputs for power and radius") self._power = power self._radius = RR(self._p**power) @@ -411,39 +419,32 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._type = 3 self._power = power - def prec(self): - """ - Returns the precision of a Type IV point. - - Not defined for Type I, II or III points. - - OUTPUT: An integer - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: d = B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]) - sage: d.prec() - 3 - """ - return self.precision() - def _custom_abs(self, x): """ Returns the absolute value of ``x`` with respect to the norm on ``Cp``. Used to simplify code, as ``x`` may be a point of a number field or a padic field. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(QQ,3) + sage: B(3)._custom_abs(9) + 1/9 """ - if self._base_type == 'padic': + if self._base_type == 'padic field': return x.abs() - return (self.prime())**(self._valuation)(x) + if x.valuation(self._ideal) == Infinity: + return 0 + if isinstance(self._ideal, NumberFieldFractionalIdeal): + return (self.prime())**(-1*x.valuation(self._ideal)/self._ideal.absolute_ramification_index()) + return (self.prime())**(-1*x.valuation(self._ideal)) def precision(self): """ - Returns the precision of a Type IV point. + Returns the precision of a type IV point. - Not defined for Type I, II, or III points. + Not defined for type I, II, or III points. OUTPUT: An integer @@ -455,20 +456,22 @@ def precision(self): 3 """ if self._type in [1,2,3]: - raise AttributeError("Type I, II, and III points do not have a precision") + raise AttributeError("type I, II, and III points do not have a precision") return self._prec + prec = precision + def power(self): - """ - Power of ``p`` such that p^power = radius. + r""" + Power of ``p`` such that `p^\text{power} = \text{radius}`. - For Type II points, always in QQ. For Type III points, - a real number. Not defined for Type I or IV points. + For type II points, always in `\QQ`. For type III points, + a real number. Not defined for type I or IV points. OUTPUT: - - An integer for Type II points - - A real number for Type III points + - An integer for type II points + - A real number for type III points EXAMPLES:: @@ -484,7 +487,7 @@ def power(self): 1.26185950714291 """ if self._type in [1,4]: - raise AttributeError("Type I and IV points do not have a power") + raise AttributeError("type I and IV points do not have a power") return self._power def radius(self): @@ -494,7 +497,7 @@ def radius(self): OUTPUT: - A non-negative real number for points Types I-III - - A list of non-negative real numbers for Type IV points + - A list of non-negative real numbers for type IV points EXAMPLES:: @@ -517,9 +520,9 @@ def diameter(self): """ Diameter function on Berkovich space. - For Type I, II, and III points, returns the radius. + For type I, II, and III points, returns the radius. - For Type IV points returns either the last radius + For type IV points returns either the last radius in the finite approximation, or if a generating function was given for the radii, the diameter is computed as the limit of the function as it's variable tends @@ -540,7 +543,7 @@ def diameter(self): sage: Q2.diameter() 9.00000000000000 - The diameter of a Type IV point is the limit of the radii:: + The diameter of a type IV point is the limit of the radii:: sage: R. = PolynomialRing(Qp(3)) sage: f = R(2) @@ -572,16 +575,16 @@ def path_distance_metric(self, other): Also referred to as the hyperbolic metric, or the big metric. - On the set of Type II, III and IV points, the path distance metric + On the set of type II, III and IV points, the path distance metric is a metric. Following Baker and Rumely, we extend - the path distance metric to Type I points x,y by p(x,x) = 0 and p(x,y) = + the path distance metric to type I points x,y by p(x,x) = 0 and p(x,y) = infinity. INPUT: - ``other`` -- A point of the same Berkovich space as this point - OUTPUT: A real number, or infinity + OUTPUT: A finite or infinite real number EXAMPLES:: @@ -595,7 +598,7 @@ def path_distance_metric(self, other): sage: Q3 = B(1) sage: Q3.path_distance_metric(Q1) - +Infinity + +infinity """ if not isinstance(other,Berkovich_Element_Cp): raise ValueError("path distance metric not defined between point of " + \ @@ -606,7 +609,7 @@ def path_distance_metric(self, other): if self == other: return 0 else: - return Infinity + return RR(Infinity) return 2*((self.join(other)).diameter().log(self.prime()))\ -self.diameter().log(self.prime())\ -other.diameter().log(other.prime()) @@ -615,12 +618,61 @@ def path_distance_metric(self, other): hyperbolic_metric = path_distance_metric + def Hsia_kernel(self, other, basepoint): + """ + The Hsia kernel of this point and ``other``, + with basepoint ``basepoint``. + + The Hsia kernel with arbitrary basepoint + is a generalization of the Hsia kernel at infinity. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point + - ``basepoint`` -- A point of the same Berkovich space as this point + + OUTPUT: A finite or infinite real number + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2,9) + sage: Q2 = B(1/27,1/27) + sage: Q3 = B(1,1/3) + sage: Q1.Hsia_kernel(Q2,Q3) + 0.111111111111111 + + :: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2,9) + sage: Q2 = B(1/2) + sage: Q3 = B(1/2) + sage: Q1.Hsia_kernel(Q2,Q3) + +infinity + + """ + if not isinstance(other,type(self)): + raise ValueError("Hsia kernel of a point of the Berkovich projective line " + \ + "takes another point of the Berkovich projective line, not %s" %(other)) + if self.parent() != other.parent(): + raise ValueError("Hsia kernel takes two points in the same Berkovich projective line") + if not isinstance(basepoint, type(self)): + raise ValueError("basepoint must be a point of the Berkovich projective line over Cp") + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich projective line") + if basepoint.type_of_point() == 1: + if self == basepoint or other == basepoint: + return RR(Infinity) + return (self.spherical_kernel(other))/ \ + (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + def small_metric(self, other): r""" Returns the small metric distance between this point and ``other``. The small metric is an extension of twice - the spherical distance on P^1(\CC_p) + the spherical distance on `P^1(\CC_p)`. INPUT: @@ -636,6 +688,13 @@ def small_metric(self, other): sage: Q1.small_metric(Q2) 0.0833333333333333 + :: + + sage: B = Berkovich_Cp_Projective(QQ, 5) + sage: Q1 = B(0, 1) + sage: Q2 = B(99) + sage: Q1.small_metric(Q2) + 1.00000000000000 """ if not isinstance(other,Berkovich_Element_Cp): raise ValueError("hyperbolic metric not defined between point " + \ @@ -665,7 +724,7 @@ def Hsia_kernel_infinity(self, other): Return the Hsia kernel at infinity of this point with ``other``. The Hsia kernel at infinity is the natural extension of the - absolute value on \CC_p to Berkovich space. + absolute value on `\CC_p` to Berkovich space. INPUT: @@ -681,13 +740,23 @@ def Hsia_kernel_infinity(self, other): sage: Q1.Hsia_kernel_infinity(Q2) 6.00000000000000 + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(4) + sage: Q2 = B(0,1.5) + sage: Q1.Hsia_kernel_infinity(Q2) + 1.50000000000000 """ return self.join(other).diameter() def center(self): r""" Returns the center of the corresponding disk (or sequence of disks) - in ``\CC_p``. + in `\CC_p`. OUTPUT: An element of the ``base_ring`` of the parent Berkovich space. @@ -703,6 +772,15 @@ def center(self): sage: C = Berkovich_Cp_Projective(3) sage: C(3,1).center() (3 + O(3^21) : 1 + O(3^20)) + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: B(a^2+4).center() + (a^2 + 4 : 1) """ if self._type == 4: return self._center_lst[:] @@ -710,7 +788,7 @@ def center(self): def type_of_point(self): """ - Returns the Type of this point of Berkovich space over ``Cp`` + Returns the type of this point of Berkovich space over ``Cp`` OUTPUT: An integer between 1 and 4 inclusive @@ -793,9 +871,9 @@ def _latex_(self): sage: B = Berkovich_Cp_Projective((3)) sage: latex(B(2,1)) - \text{Type 2 Point of } \text{Projective Berkovich line over } - \Bold{C}_{3} \text{equivalent to the disk centered at (2 + O(3^20) : 1 + O(3^20)) - of radius 1.00000000000000 in } \Bold{C}_3 + \text{type 2 Point of } \text{Projective Berkovich line over } + \Bold{C}_{3} \text{equivalent to the disk centered at + (2 + O(3^20) : 1 + O(3^20)) of radius 1.00000000000000 in } \Bold{C}_3 """ from sage.misc.latex import latex if self._type == 1: @@ -806,41 +884,44 @@ def _latex_(self): else: text = "the sequence of disks with centers %s } " %self._center_lst[:2] + \ r"\ldots \text{ and radii %s } \ldots" %self._radius_lst[:2] - return r"\text{Type %s Point of }" %(self._type) \ + return r"\text{type %s Point of }" %(self._type) \ + latex(self.parent()) + r"\text{equivalent to " + text class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" Element class of the Berkovich affine line over `\CC_p` - Elements are categorized into four Types, represented by specific data: + Elements are categorized into four types, represented by specific data: - - Type I points are represented by a center in `\QQ_p` or a finite extension + - Type I points are represented by a center in the ``base`` of the parent Berkovich space, + which is `\QQ_p`, a finite extension of `\QQ_p`, or a number field. - - Type II points are represented by a center in `\QQ_p` and a rational power of `p` + - Type II points are represented by a center in the ``base`` of the parent Berkovich space, + and a rational power of `p`. - - Type III points are represented by a center in `\QQ_p` and a radius in `[0,\infty)` + - Type III points are represented by a center in the ``base`` of the parent Berkovich space, + and a radius in `[0,\infty)`. - - Type IV points are represented by a finite list of centers in `\QQ_p` and - a finite list of radii in `[0,\infty)`. + - Type IV points are represented by a finite list of centers in the ``base`` of the parent + Berkovich space and a finite list of radii in `[0,\infty)`. INPUT: - - ``center`` -- For Type I, II, and III points, the center of the - corresponding disk in `\CC_p`. Must be an element of `\QQ_p`, a finite extension - of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers - used to approximate the point or a univariate function that computes the centers - (computation starts at 1). + - ``center`` -- For type I, II, and III points, the center of the + corresponding disk in `\CC_p`. If the parent Berkovich space was created using a number field + `K`, then ``center`` must be an element of `K`. Otherwise, ``center`` must be an element of a + p-adic field. For type IV points, can be a list of centers used to approximate the point or a + univariate function that computes the centers (computation starts at 1). - - ``radius`` -- (optional) For Type I, II, and III points, the radius of the - corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, + - ``radius`` -- (optional) For type I, II, and III points, the radius of the + corresponding disk in ``Cp``. Must coerce into the real numbers. For type IV points, can be a list of radii used to approximate the point or a univariate function that - computes the radii (computation starts at 1). + computes the radii (computation starts at 1). - - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies + - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies the power of ``p`` such that p^ ``power`` = radius - - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point + - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point - ``error_check`` -- (default: True) If error checking should be run on input. If input is correctly formatted, can be set to ``False`` for better performance. @@ -870,7 +951,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: c = B(2,1.6); c Type III point centered at 2 + O(3^20) of radius 1.60000000000000 - Some Type II points may be mistaken for Type III points:: + Some type II points may be mistaken for type III points:: sage: b = B(3, 3**0.5); b Type III point centered at 3 + O(3^21) of radius 1.73205080756888 @@ -910,8 +991,8 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: R. = QQ[] sage: A. = NumberField(x^3+20) - sage: v = A.valuation(3) - sage: B = Berkovich_Cp_Projective(A,v) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Projective(A,ideal) sage: Q1 = B(a); Q1 Type I point centered at (a : 1) @@ -965,6 +1046,12 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: B(w,power=1) Type II point centered at w + O(w^41) of radius 5^1 + sage: C = Berkovich_Cp_Projective(QQ, 3) + sage: C(Q1) + Traceback (most recent call last): + ... + ValueError: cannot convert from Berkovich space over + a padic field to Berkovich space over a number field """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space @@ -973,13 +1060,17 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._p = parent.prime() self._base_space = parent.base() self._base_type = parent._base_type - self._valuation = parent._valuation + self._ideal = parent._ideal #if this is a point of projective berkovich space, we do the conversion if isinstance(center, Berkovich_Element_Cp_Projective): if center._base_type != self._base_type: raise ValueError('cannot convert from Berkovich space over ' + \ - 'a %s to Berkovich space over a %s field' %center._base_type, self._base_type) + 'a %s to Berkovich space over a %s' %center._base_type, self._base_type) + if self._base_type == 'number field': + if center._base_space != self._base_space: + raise ValueError('cannot implicitly convert from %s to %s' \ + %(center._base_space, self._base_space)) if (center.prime() == self._p): try: center.center().dehomogenize(1) @@ -988,7 +1079,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check flag = True if flag: raise ValueError("the point at infinity of Berkovich " + \ - "Projective space does not convert to Berkovich Affine space") + "projective space does not convert to Berkovich Affine space") self._center = center.center()[0] self._radius = center._radius self._power = center._power @@ -1004,7 +1095,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("tried to convert from a point of Berkovich space " + \ + raise ValueError("cannot convert from a point of Berkovich space " + \ "over Cp(%s) to a " %center._base_space.base_ring().prime() + \ "point of Berkovich space over Cp(%s)" %(parent.base().prime())) @@ -1017,8 +1108,8 @@ def center(self): OUTPUT: - - For Type I-III points, a point of ``Cp`` - - For Type IV points, a list of points of ``Cp`` + - For type I-III points, a point of ``Cp`` + - For type IV points, a list of points of ``Cp`` EXAMPLES:: @@ -1058,7 +1149,7 @@ def __eq__(self, other): if stype == otype and stype == 1: return self.center() == other.center() elif stype == otype and stype == 4: - raise NotImplementedError("Equality for Type IV points not yet implemented") + raise NotImplementedError("Equality for type IV points not yet implemented") elif stype in [2,3] and otype in [2,3]: if self.radius() != other.radius(): return False @@ -1073,27 +1164,40 @@ def __hash__(self): EXAMPLES:: - sage: B = Berkovich_Cp_Projective(3) + sage: B = Berkovich_Cp_Affine(3) sage: Q1 = B(1, RR(3**(1/2))) sage: Q2 = B(1, 3**(1/2)) sage: hash(Q1) == hash(Q2) True + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(a^2+1, 2) + sage: Q2 = B(0, 2) + sage: hash(Q1) == hash(Q2) + True """ - if self.type_of_point() != 4: - return hash(str(self.center())+str(self.radius())) - return hash(str(self.center()[self.precision()])+str(self.radius()[self.precision()])) + if self.type_of_point() == 1: + return hash(str(self.center())) + elif self.type_of_point() == 4: + raise ValueError('hash not defined for type IV points') + return hash(str(self.radius())) def partial_order(self,other): - """ + r""" The standard partial order on Berkovich space Roughly, the partial order corresponds to containment of - the corresponding disks in ``Cp``. + the corresponding disks in `\CC_p`. - For example, let x and y be points of Type II or III. - If x has center ``c1`` and radius ``r1`` and y has center - ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) - is a subset of D(c2,r2) in ``Cp``. + For example, let x and y be points of type II or III. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, x < y if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2) in `\CC_p`. INPUT: @@ -1197,7 +1301,7 @@ def join(self, other, basepoint="infty"): if self.parent() != other.parent(): raise ValueError("join takes two points in the same Berkovich Affine line") if self.type_of_point() == 4 or other.type_of_point() == 4: - raise NotImplementedError("join with Type IV points not implemented") + raise NotImplementedError("join with type IV points not implemented") parent = self.parent() base = self._base_space @@ -1214,7 +1318,7 @@ def join(self, other, basepoint="infty"): if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich Affine line") if basepoint.type_of_point() == 4: - raise NotImplementedError("join not implemented for Type IV basepoint") + raise NotImplementedError("join not implemented for type IV basepoint") proj_basepoint = B(basepoint) return parent(proj_self.join(proj_other, proj_basepoint)) @@ -1226,14 +1330,14 @@ def involution_map(self): The involution map is the extension of the map ``z |-> 1/z`` map on ``Cp`` to Berkovich space. - For Affine Berkovich Space, not defined for the Type I + For Affine Berkovich Space, not defined for the type I point centered at 0. OUTPUT: A point of the same Berkovich space EXAMPLES: - The involution map is 1/z on Type I points:: + The involution map is 1/z on type I points:: sage: B = Berkovich_Cp_Affine((3)) sage: Q1 = B(1/2) @@ -1258,7 +1362,7 @@ def involution_map(self): if self.type_of_point() == 1: if center == 0: - raise ValueError("involution map not deffined on Affine Type I point centered at 0") + raise ValueError("involution map not deffined on affine type I point centered at 0") return parent(1/center) zero = parent(QQ(0)) @@ -1334,15 +1438,15 @@ def potential_kernel(self, other, basepoint): """ The potential kernel of this point with ``other``, with basepoint ``basepoint``. - The potential kernel is the hyperbolic distance between the basepoint and the join - of this point with ``other`` relative to the basepoint. + The potential kernel is the hyperbolic distance between ``basepoint`` and the join + of this point with ``other`` relative to ``basepoint``. INPUT: - ``other`` -- A point of the same Berkovich space as this point - ``basepoint`` -- A point of the same Berkovich space as this point - OUTPUT: A real number or the infinity symbol 'oo' + OUTPUT: A finite or infinite real number EXAMPLES:: @@ -1394,58 +1498,11 @@ def spherical_kernel(self,other): gauss_point = (self.parent())(RR(0),RR(1)) w = self.join(other,gauss_point) dist = gauss_point.path_distance_metric(w) - if dist == "oo": + if dist == Infinity: return 0 return (self.prime())**(-1*dist) - def Hsia_kernel(self, other, basepoint): - """ - The Hsia kernel of this point and ``other``, with basepoint ``basepoint``. - - The Hsia kernel is a generalization of the Hsia kernel at - infinity. - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point - - OUTPUT: A real number or the infinity symbol 'oo' - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2,9) - sage: Q2 = B(1/27,1/27) - sage: Q3 = B(1,1/3) - sage: Q1.Hsia_kernel(Q2,Q3) - 0.111111111111111 - - :: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2,9) - sage: Q2 = B(1/2) - sage: Q3 = B(1/2) - sage: Q1.Hsia_kernel(Q2,Q3) - 'oo' - - """ - if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("Hsia kernel of a point of the Berkovich Affine line " + \ - "takes another point of the Berkovich Affine line, not %s" %(other)) - if self.parent() != other.parent(): - raise ValueError("Hsia kernel takes two points in the same Berkovich Affine line") - if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise ValueError("basepoint must be a point of the Berkovich Affine line over Cp") - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") - if basepoint.type_of_point() == 1: - if self == basepoint or other == basepoint: - return "oo" - return (self.spherical_kernel(other))/ \ - (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) - - def diameter(self, basepoint="oo"): + def diameter(self, basepoint=Infinity): """ Generalized diameter function. @@ -1461,7 +1518,7 @@ def diameter(self, basepoint="oo"): - ``basepoint`` -- (default = Infinity) A point of the same Berkovich space as this point - OUTPUT: A real number or Infinity + OUTPUT: A finite or infinite real number EXAMPLES:: @@ -1474,51 +1531,50 @@ def diameter(self, basepoint="oo"): :: sage: Q2.diameter(Q2) - 'oo' + +infinity """ - if basepoint == "oo": + if basepoint == Infinity: return super().diameter() - else: - return self.Hsia_kernel(self,basepoint) + return self.Hsia_kernel(self,basepoint) class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" - Element class of the Berkovich Projective line over `\CC_p`. + Element class of the Berkovich projective line over `\CC_p`. Elements are categorized into four Types, which are represented as follows: - - Type I points are represented by a center in Projective Space of dimension 1 over + - Type I points are represented by a center in projective Space of dimension 1 over `\QQ_p` or over a finite extension of `\QQ_p`. - - Type II points are represented by a center in Projective Space of dimension 1 over + - Type II points are represented by a center in projective Space of dimension 1 over `\QQ_p` or over a finite extension of `\QQ_p` and a rational power of `p`. Type II points cannot be centered at the point at infinity. - - Type III points are represented by a center in Projective Space of dimension 1 over + - Type III points are represented by a center in projective Space of dimension 1 over `\QQ_p` or over a finite extension of `\QQ_p` and a radius in `[0,\infty)`. Type III points are cannot be centered at the point at infinity. - - Type IV points are represented by finite list of centers in Projective Space of dimension 1 over + - Type IV points are represented by finite list of centers in projective Space of dimension 1 over `\QQ_p` or over a finite extension of `\QQ_p` and by a finite list of radii in `[0,\infty)`. None of the centers can be the point at infinity. INPUT: - - ``center`` -- For Type I, II, and III points, the center of the + - ``center`` -- For type I, II, and III points, the center of the corresponding disk in `P^1(\CC_p)`. Must be an element of `\QQ_p`, a finite extension - of `\QQ_p`, or coerce into `\QQ_p`. For Type IV points, can be a list of centers + of `\QQ_p`, or coerce into `\QQ_p`. For type IV points, can be a list of centers used to approximate the point or a univariate function that computes the centers (computation starts at 1). - - ``radius`` -- (optional) For Type I, II, and III points, the radius of the - corresponding disk in ``Cp``. Must coerce into the real numbers. For Type IV points, + - ``radius`` -- (optional) For type I, II, and III points, the radius of the + corresponding disk in ``Cp``. Must coerce into the real numbers. For type IV points, can be a list of radii used to approximate the point or a univariate function that computes the radii (computation starts at 1). - - ``power`` -- (optional) Rational number. Used for constructing Type II points; specifies + - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies the power of ``p`` such that p^ ``power`` = radius. - - ``prec`` -- (default: 20) The number of disks to be used to approximate a Type IV point. + - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point. - ``error_check`` -- (default: True) If error checking should be run on input. If input is correctly formatted, can be set to ``False`` for better performance. @@ -1592,13 +1648,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._p = parent.prime() self._base_space = parent.base() self._base_type = parent._base_type - self._valuation = parent._valuation + self._ideal = parent._ideal #conversion from Affine points is handled in this constructor if isinstance(center, Berkovich_Element_Cp_Affine): if center._base_type != self._base_type: raise ValueError('cannot convert from Berkovich space over ' + \ - 'a %s field to Berkovich space over a %s field' %(center._base_type, self._base_type)) + 'a %s to Berkovich space over a %s' %(center._base_type, self._base_type)) if (center.prime() == self._p): self._center = (self._base_space)(center._center) self._radius = center._radius @@ -1615,7 +1671,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._center_lst = center_lst return else: - raise ValueError("tried to convert from a point of Berkovich space over " + \ + raise ValueError("cannot convert from a point of Berkovich space over " + \ "Cp(%s) to a point of Berkovich space over Cp(%s)" \ %(center._base_space.base_ring().prime(),parent.base().prime())) @@ -1628,8 +1684,8 @@ def center(self): OUTPUT: - - For Type I-III points, a point of `P^1(\CC_p)` - - For Type IV points, a list of points of `P^1(\CC_p)` + - For type I-III points, a point of `P^1(\CC_p)` + - For type IV points, a list of points of `P^1(\CC_p)` EXAMPLES:: @@ -1669,7 +1725,7 @@ def __eq__(self, other): if stype == otype and stype == 1: return self.center() == other.center() elif stype == otype and stype == 4: - raise NotImplementedError("equality for Type IV points not yet implemented") + raise NotImplementedError("equality for type IV points not implemented") elif stype in [2,3] and otype in [2,3]: if self.radius() != other.radius(): return False @@ -1692,11 +1748,23 @@ def __hash__(self): sage: Q2 = B([1,1], 3**(1/2)) sage: hash(Q1) == hash(Q2) True + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(a^2+1, 2) + sage: Q2 = B(0, 2) + sage: hash(Q1) == hash(Q2) + True """ - if self.type_of_point() != 4: - return hash(str(self.center().normalize_coordinates())+str(self.radius())) - return hash(str((self.center()[self.precision()]).normalize_coordinates())\ - +str(self.radius()[self.precision()])) + if self.type_of_point() == 1: + return hash(str(self.center().normalize_coordinates())) + elif self.type_of_point() == 4: + raise ValueError('hash not defined for type IV points') + return hash(str(self.radius())) def partial_order(self,other): """ @@ -1705,7 +1773,7 @@ def partial_order(self,other): Roughly, the partial order corresponds to containment of the corresponding disks in ``Cp``. - For example, let x and y be points of Type II or III. + For example, let x and y be points of type II or III. If x has center ``c1`` and radius ``r1`` and y has center ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) is a subset of D(c2,r2) in ``Cp``. @@ -1749,10 +1817,10 @@ def partial_order(self,other): """ if not isinstance(other,Berkovich_Element_Cp_Projective): raise ValueError("partial order takes another point of " + \ - "the Berkovich Projective line, not %s" %(other)) + "the Berkovich projective line, not %s" %(other)) if self.parent() != other.parent(): raise ValueError("partial order takes another point of the " + \ - "same Berkovich Projective line") + "same Berkovich projective line") #if self or other is infinity, we apply the involution map parent = self.parent() @@ -1822,10 +1890,14 @@ def join(self, other, basepoint="infty"): sage: Q1.join(Q2) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + :: + sage: Q3 = B(5) sage: Q3.join(Q1) Type II point centered at (2 + 3 + O(3^20) : 1 + O(3^20)) of radius 3^0 + :: + sage: Q3.join(Q1, basepoint=Q2) Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 @@ -1846,16 +1918,35 @@ def join(self, other, basepoint="infty"): sage: Q1.join(Q2, Q7) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q8 = B(0, power=1/3) + sage: Q9 = B(0, power=1/2) + sage: Q8.join(Q9) + Type II point centered at (0 : 1 + O(3^20)) of radius 3^1/2 + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.prime_above(3) + sage: C = Berkovich_Cp_Projective(A, ideal) + sage: Q10 = C(a, 1/9) + sage: Q10.join(Q9) + Traceback (most recent call last): + ... + ValueError: join takes two points in the same Berkovich projective line + + sage: Q11 = C(0, 1/3) + sage: Q11.join(Q10) + Type II point centered at (0 : 1) of radius 3^0 """ parent = self.parent() if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("join of a point of the Berkovich Projective " + \ - "line takes another point of the Berkovich Projective line, not %s" %(other)) + raise ValueError("join of a point of the Berkovich projective " + \ + "line takes another point of the Berkovich projective line, not %s" %(other)) if other.parent() != parent: - raise ValueError("join takes two points in the same Berkovich Projective line") + raise ValueError("join takes two points in the same Berkovich projective line") - #if either self or other is Type IV, we use the last disk in the approximation + #if either self or other is type IV, we use the last disk in the approximation if self.type_of_point() == 4: new_center = self.center()[self.prec()-1] @@ -1874,14 +1965,20 @@ def join(self, other, basepoint="infty"): if self == infty or other == infty: return infty dist = self._custom_abs(self.center()[0] - other.center()[0]) - return parent(self.center(),max(dist,self.radius(),other.radius())) #TODO optimize for Type II + maximum = max(dist, self.radius(), other.radius()) + #optimize for when self or other are type II + if maximum == self.radius() and self.type_of_point() == 2: + return parent(self.center(), power=self.power()) + if maximum == other.radius() and other.type_of_point() == 2: + return parent(self.center(), power=other.power()) + return parent(self.center(), maximum) if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint for join must be a point of the Berkovich Projective line over Cp") + raise ValueError("basepoint for join must be a point of the Berkovich projective line over Cp") if basepoint.parent() != parent: - raise ValueError("basepoint must be a point of the same Berkovich Projective line") + raise ValueError("basepoint must be a point of the same Berkovich projective line") - #if the basepoint is Type IV, we use the last disk in the approximation + #if the basepoint is type IV, we use the last disk in the approximation if basepoint.type_of_point() == 4: new_center = other.center()[other.prec()-1] @@ -1954,7 +2051,7 @@ def involution_map(self): EXAMPLES: - The involution map is 1/z on Type I points:: + The involution map is 1/z on type I points:: sage: B = Berkovich_Cp_Projective((3)) sage: Q1 = B(1/2) @@ -1972,7 +2069,6 @@ def involution_map(self): sage: Q3 = B(1/3,1/3) sage: Q3.involution_map() Type II point centered at (3 + O(3^21) : 1 + O(3^20)) of radius 3^-3 - """ parent = self.parent() center = self.center() @@ -2097,13 +2193,13 @@ def potential_kernel(self, other, basepoint): """ if not isinstance(other,Berkovich_Element_Cp_Projective): raise ValueError("potential kernel of a point of the Berkovich " + \ - "Projective line takes another point of the Berkovich Projective line, not %s" %(other)) + "projective line takes another point of the Berkovich projective line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("potential kernel takes two points in the same Berkovich Projective line") + raise ValueError("potential kernel takes two points in the same Berkovich projective line") if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint must be a point of the Berkovich Projective line over Cp") + raise ValueError("basepoint must be a point of the Berkovich projective line over Cp") if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Projective line") + raise ValueError("basepoint must be a point of the same Berkovich projective line") return basepoint.path_distance_metric((self.join(other,basepoint))) def spherical_kernel(self,other): @@ -2111,7 +2207,7 @@ def spherical_kernel(self,other): The spherical kernel of this point with ``other``. The spherical kernel is one possible extension of the spherical - distance on `P^1(\CC_p)` to the Projective Berkovich line. + distance on `P^1(\CC_p)` to the projective Berkovich line. INPUT: @@ -2127,69 +2223,25 @@ def spherical_kernel(self,other): sage: Q1.spherical_kernel(Q2) 0.500000000000000 + :: + + sage: Q3 = B(2) + sage: Q3.spherical_kernel(Q3) + 0 """ if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("spherical kernel of a point of the Berkovich Projective " + \ - "line takes another point of the Berkovich Projective line, not %s" %(other)) + raise ValueError("spherical kernel of a point of the Berkovich projective " + \ + "line takes another point of the Berkovich projective line, not %s" %(other)) if self.parent() != other.parent(): - raise ValueError("spherical kernel takes two points in the same Berkovich Projective line") + raise ValueError("spherical kernel takes two points in the same Berkovich projective line") gauss_point = (self.parent())(RR(0),RR(1)) w = self.join(other,gauss_point) dist = gauss_point.path_distance_metric(w) - if dist == "oo": + if dist == Infinity: return 0 return (self.prime())**(-1*dist) - def Hsia_kernel(self, other, basepoint): - """ - The Hsia kernel of this point and ``other``, - with basepoint ``basepoint``. - - The Hsia kernel with arbitrary basepoint - is a generalization of the Hsia kernel at infinity. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point - - OUTPUT: A real number or infinity - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2,9) - sage: Q2 = B(1/27,1/27) - sage: Q3 = B(1,1/3) - sage: Q1.Hsia_kernel(Q2,Q3) - 0.111111111111111 - - :: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2,9) - sage: Q2 = B(1/2) - sage: Q3 = B(1/2) - sage: Q1.Hsia_kernel(Q2,Q3) - +Infinity - - """ - if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("Hsia kernel of a point of the Berkovich Projective line " + \ - "takes another point of the Berkovich Projective line, not %s" %(other)) - if self.parent() != other.parent(): - raise ValueError("Hsia kernel takes two points in the same Berkovich Projective line") - if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint must be a point of the Berkovich Projective line over Cp") - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Projective line") - if basepoint.type_of_point() == 1: - if self == basepoint or other == basepoint: - return Infinity - return (self.spherical_kernel(other))/ \ - (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) - - def diameter(self, basepoint="oo"): + def diameter(self, basepoint=Infinity): """ Generalized diameter function. @@ -2203,7 +2255,7 @@ def diameter(self, basepoint="oo"): INPUT: - ``basepoint`` -- (default = Infinity) A point of the same - Berkovich space as this point, or the string 'oo' + Berkovich space as this point, or infinity OUTPUT: A real number or infinity @@ -2218,9 +2270,9 @@ def diameter(self, basepoint="oo"): :: sage: Q2.diameter(Q2) - +Infinity + +infinity """ - if basepoint == "oo": + if basepoint == Infinity: return super().diameter() else: return self.Hsia_kernel(self,basepoint) @@ -2236,6 +2288,29 @@ class Berkovich_Cp(Berkovich): Abstract parent class for Berkovich space over ``Cp``. """ + def residue_characteristic(self): + """ + The residue characteristic of the ``base``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B.prime() + 3 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Affine(A, ideal) + sage: B.residue_characteristic() + 7 + """ + return self._p + + prime = residue_characteristic + def __eq__(self,right): """ Equality operator. @@ -2247,10 +2322,34 @@ def __eq__(self,right): sage: C = Berkovich_Cp_Affine(A) sage: B == C True + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^2+1) + sage: A_ideal = A.prime_above(2) + sage: B. = NumberField(x^4+1) + sage: B_ideal = B.prime_above(2) + sage: C = Berkovich_Cp_Projective(A, A_ideal) + sage: D = Berkovich_Cp_Projective(B, B_ideal) + sage: C == D + False + + :: + + sage: C = Berkovich_Cp_Affine(A, A_ideal) + sage: D = Berkovich_Cp_Affine(B, B_ideal) + sage: C == D + False """ if not isinstance(right, Berkovich_Cp): return False - return self.prime() == right.prime() + if self._base_type != right._base_type: + return False + if self._base_type == 'padic field': + return self.prime() == right.prime() + else: + return self.base() == right.base() def __ne__(self,right): """ @@ -2276,8 +2375,20 @@ class Berkovich_Cp_Affine(Berkovich_Cp): INPUT: - - ``base`` - The prime ``p``. Alternative, can be `\QQ_p` or a finite extension. - This allows for more control over automated conversion of centers of points. + - ``base`` -- Three cases: + + * a prime number `p`. Centers of elements are then represented + as points of `\QQ_p`. + + * `\QQ_p` or a finite extension of `\QQ_p`. Centers of elements + are then represented as points of ``base``. + + * A number field `K`. Centers of elements are then represented + as points of `K`. + + - ``ideal`` -- (optional) a prime ideal of ``base``. Must be + specified if a number field is passed to ``base``, otherwise + it is ignored. EXAMPLES:: @@ -2303,35 +2414,50 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: B = Berkovich_Cp_Affine(Qp(3,1000)); B Affine Berkovich line over Cp(3) of precision 1000 + + For exact computation, a number field can be used:: + + sage: R. = QQ[] + sage: A. = NumberField(x^3 + 20) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Affine(A, ideal); B + Affine Berkovich line over Cp(3), with base Number + Field in a with defining polynomial x^3 + 20 """ Element = Berkovich_Element_Cp_Affine - def __init__(self, base, valuation=None): + def __init__(self, base, ideal=None): if base in ZZ: if base.is_prime(): base = Qp(base) #TODO chance to Qpbar else: raise ValueError("non-prime pased into Berkovich space") if is_NumberField(base): - if valuation == None: - raise ValueError('passed a number field but not a valuation') - from sage.rings.padics.padic_valuation import pAdicValuation_base - if not isinstance(valuation, pAdicValuation_base): - raise ValueError('valuation was not a padic valuation') - if valuation.domain() != base: - raise ValueError('passed number field ' + \ - '%s but valuation defined over %s' %(base, valuation.domain())) - prime = valuation.p() - self._base_type = 'number' + if ideal == None: + raise ValueError('passed a number field but not an ideal') + if base is not QQ: + if not isinstance(ideal, NumberFieldFractionalIdeal): + raise ValueError('ideal was not an ideal of a number field') + if ideal.number_field() != base: + raise ValueError('passed number field ' + \ + '%s but ideal was an ideal of %s' %(base, ideal.number_field())) + prime = ideal.smallest_integer() + else: + if ideal not in QQ: + raise ValueError('ideal was not an element of QQ') + prime = ideal + if not ideal.is_prime(): + raise ValueError('passed non prime ideal') + self._base_type = 'number field' elif is_pAdicField(base): #TODO change base to Qpbar(prime) prime = base.prime() - valuation = base.valuation() - self._base_type = 'padic' + ideal = None + self._base_type = 'padic field' else: raise ValueError("base of Berkovich Space must be a padic field " + \ "or a number field") - self._valuation = valuation + self._ideal = ideal self._p = prime Parent.__init__(self, base=base, category=TopologicalSpaces()) @@ -2347,17 +2473,7 @@ def residue_characteristic(self): """ return self._p - def prime(self): - """ - Short hand for ``residue_characteristic``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B.prime() - 3 - """ - return self._p + prime = residue_characteristic def _repr_(self): """ @@ -2369,8 +2485,13 @@ def _repr_(self): sage: B Affine Berkovich line over Cp(3) of precision 20 """ - return "Affine Berkovich line over Cp(%s) of precision %s" \ - %(self.prime(),self.base().precision_cap()) + if self._base_type == 'padic field': + return "Affine Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ + self.base().precision_cap()) + else: + return "Affine Berkovich line over Cp(%s), with base %s" %(self.prime(),\ + self.base()) + def _latex_(self): r""" @@ -2386,16 +2507,16 @@ def _latex_(self): class Berkovich_Cp_Projective(Berkovich_Cp): r""" - The Berkovich Projective line over `\CC_p`. + The Berkovich projective line over `\CC_p`. - The Berkovich Projective line is the one-point compactification + The Berkovich projective line is the one-point compactification of the Berkovich Affine line. INPUT: - ``base`` -- Three cases: - * a prime number ``p``. Centers of elements are then represented + * a prime number `p`. Centers of elements are then represented as points of projective space of dimension 1 over `\QQ_p`. * `\QQ_p` or a finite extension of `\QQ_p`. Centers of elements @@ -2405,7 +2526,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): * A number field `K`. Centers of elements are then represented as points of projective space of dimension 1 over ``base``. - - ``valuation`` -- (optional) a valuation on ``base``. Must be + - ``ideal`` -- (optional) a prime ideal of ``base``. Must be specified if a number field is passed to ``base``, otherwise it is ignored. @@ -2431,12 +2552,12 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Note that this point has very low precision, as S has low precision cap. Berkovich space can also be created over - a number field, as long as a valuation is specified:: + a number field, as long as an ideal is specified:: sage: R. = QQ[] sage: A. = NumberField(x^2+1) - sage: v = A.valuation(a+1) - sage: B = Berkovich_Cp_Projective(A,v); B + sage: ideal = A.prime_above(2) + sage: B = Berkovich_Cp_Projective(A,ideal); B Projective Berkovich line over Cp(2), with base Number Field in a with defining polynomial x^2 + 1 @@ -2446,7 +2567,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Element = Berkovich_Element_Cp_Projective - def __init__(self, base, valuation=None): + def __init__(self, base, ideal=None): if base in ZZ: if base.is_prime(): base = ProjectiveSpace(Qp(base),1) @@ -2458,65 +2579,47 @@ def __init__(self, base, valuation=None): base = ProjectiveSpace(base, 1) from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(base): - raise ValueError("base of Projective Berkovich Space must be Projective Space") + raise ValueError("base of projective Berkovich space must be projective space") if not (is_pAdicField(base.base_ring())): if not (is_NumberField(base.base_ring())): - raise ValueError("base of Projective Berkovich Space must be " + \ - "Projective Space over Qp or a number field") + raise ValueError("base of projective Berkovich space must be " + \ + "projective space over Qp or a number field") else: - if valuation == None: - raise ValueError('passed a number field but not a valuation') - from sage.rings.padics.padic_valuation import pAdicValuation_base - if not isinstance(valuation, pAdicValuation_base): - raise ValueError('valuation was not a padic valuation') - if valuation.domain() != base.base_ring(): - raise ValueError('passed number field ' + \ - '%s but valuation defined over %s' %(base.base_ring(), valuation.domain())) - prime = valuation.p() - self._base_type = 'number' + if ideal == None: + raise ValueError('passed a number field but not an ideal') + if base.base_ring() is not QQ: + if not isinstance(ideal, NumberFieldFractionalIdeal): + raise ValueError('ideal was not a number field ideal') + if ideal.number_field() != base.base_ring(): + raise ValueError('passed number field ' + \ + '%s but ideal was an ideal of %s' %(base.base_ring(), ideal.number_field())) + prime = ideal.smallest_integer() + else: + if ideal not in QQ: + raise ValueError('ideal was not an element of QQ') + prime = ideal + if not ideal.is_prime(): + raise ValueError('passed non prime ideal') + self._base_type = 'number field' else: - valuation = base.base_ring().valuation() prime = base.base_ring().prime() - self._base_type = 'padic' + ideal = None + self._base_type = 'padic field' if base.ambient_space() != base: - raise ValueError("base of Projective Berkovich Space must be " + \ - "Projective Space over Qp or a number field") + raise ValueError("base of projective Berkovich space must be " + \ + "projective space over Qp or a number field") if base.dimension() != 1: - raise ValueError("base of Projective Berkovich Space must be " + \ - "Projective Space of dimension 1 over Qp or a number field") + raise ValueError("base of projective Berkovich space must be " + \ + "projective space of dimension 1 over Qp or a number field") self._p = prime - self._valuation = valuation + self._ideal = ideal Parent.__init__(self, base = base, category=TopologicalSpaces()) - def residue_characteristic(self): - """ - The residue characteristic of the ``base``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: B.residue_characteristic() - 3 - """ - return self._p - - def prime(self): - """ - Short hand for ``residue_characteristic``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: B.prime() - 3 - """ - return self._p - def base_ring(self): r""" The base of this Berkovich Space. - OUTPUT: A Projective Space of dimension 1 over `\QQ_p` + OUTPUT: A projective Space of dimension 1 over `\QQ_p` or a finite extension. EXAMPLES:: @@ -2547,11 +2650,11 @@ def _repr_(self): sage: R. = QQ[] sage: A. = NumberField(x^2+1) - sage: v = A.valuation(a+1) + sage: v = A.ideal(a+1) sage: B = Berkovich_Cp_Projective(A,v); B Projective Berkovich line over Cp(2), with base Number Field in a with defining polynomial x^2 + 1 """ - if self._base_type == 'padic': + if self._base_type == 'padic field': return "Projective Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ self.base().base_ring().precision_cap()) else: From 7f2f3d22ea4f335ccb5156496a5984e566793ff8 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 15 Jul 2020 15:25:38 -0400 Subject: [PATCH 056/379] 29844: fixed documentation errors --- src/sage/schemes/berkovich/berkovich_space.py | 163 +++++++++--------- 1 file changed, 86 insertions(+), 77 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index f55d9e79ecd..0d77fa47579 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -10,10 +10,9 @@ :class:`Berkovich_Cp_Projective`, which implement the affine and projective lines, respectively. -:class:`Berkovich_Cp_Affine` takes as input a prime `p` or a finite -extension of `\QQ_p`, while :class:`Berkovich_Cp_Projective` takes -as input a prime `p` or projective space of dimension 1 over a -finite extension of `\QQ_p`. +:class:`Berkovich_Cp_Affine` and :class:`Berkovich_Cp_Projective` +take as input one of the following: the prime `p`, a finite +extension of `\QQ_p`, or a number field and a place. AUTHORS: @@ -82,8 +81,19 @@ class Berkovich_Element(Element): class Berkovich_Element_Cp(Berkovich_Element): r""" The abstract parent class for any element of Berkovich space over `\CC_p`. - This class should never be instantiated, instead Berkovich_Element_Cp_Affine - or Berkovich_Element_Cp_Projective should be used. + This class should never be instantiated, instead use :class:`Berkovich_Element_Cp_Affine` + or :class:`Berkovich_Element_Cp_Projective`. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(2) + Type I point centered at 2 + O(3^20) + + :: + + sage: B(0,1) + Type II point centered at 0 of radius 3^0 """ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, error_check=True): @@ -470,7 +480,7 @@ def power(self): OUTPUT: - - An integer for type II points + - A rational for type II points - A real number for type III points EXAMPLES:: @@ -570,15 +580,15 @@ def diameter(self): return self._radius def path_distance_metric(self, other): - """ + r""" Returns the path distance metric distance between ``self`` and ``other``. Also referred to as the hyperbolic metric, or the big metric. On the set of type II, III and IV points, the path distance metric is a metric. Following Baker and Rumely, we extend - the path distance metric to type I points x,y by p(x,x) = 0 and p(x,y) = - infinity. + the path distance metric to type I points `x`, `y` by `\rho(x,x) = 0` and `\rho(x,y) = + \infty`. INPUT: @@ -919,7 +929,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): computes the radii (computation starts at 1). - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies - the power of ``p`` such that p^ ``power`` = radius + the power of ``p`` such that `p^\text{power}` = radius - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point @@ -1196,8 +1206,8 @@ def partial_order(self,other): For example, let x and y be points of type II or III. If x has center `c_1` and radius `r_1` and y has center - `c_2` and radius `r_2`, x < y if and only if `D(c_1,r_1)` - is a subset of `D(c_2,r_2) in `\CC_p`. + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2)` in `\CC_p`. INPUT: @@ -1247,13 +1257,13 @@ def partial_order(self,other): proj_other = B(other) return proj_self.partial_order(proj_other) - def join(self, other, basepoint="infty"): + def join(self, other, basepoint=Infinity): """ Computes the join of this point and ``other`` with respect to ``basepoint``. The join is first point that lies on the interesection - of the path from self to basepoint and the path from other to - basepoint. + of the path from this point to ``basepoint`` and the path from ``other`` to + ``basepoint``. INPUT: @@ -1309,7 +1319,7 @@ def join(self, other, basepoint="infty"): proj_self = B(self) proj_other = B(other) - if basepoint == "infty": + if basepoint == Infinity: return parent(proj_self.join(proj_other)) if not isinstance(basepoint, Berkovich_Element_Cp_Affine): @@ -1324,11 +1334,11 @@ def join(self, other, basepoint="infty"): def involution_map(self): - """ + r""" Returns the image of this point under the involution map. The involution map is the extension of the map ``z |-> 1/z`` map - on ``Cp`` to Berkovich space. + on `\CC_p` to Berkovich space. For Affine Berkovich Space, not defined for the type I point centered at 0. @@ -1394,7 +1404,7 @@ def involution_map(self): def contained_in_interval(self, start, end): """ - Checks if this point is an element of the interval [``start``,``end``]. + Checks if this point is an element of the interval [``start``, ``end``]. INPUT: @@ -1403,7 +1413,7 @@ def contained_in_interval(self, start, end): OUTPUT: - - ``True`` if this point is an element of [``start``,``end``] + - ``True`` if this point is an element of [``start``, ``end``] - ``False`` otherwise EXAMPLES:: @@ -1503,11 +1513,11 @@ def spherical_kernel(self,other): return (self.prime())**(-1*dist) def diameter(self, basepoint=Infinity): - """ + r""" Generalized diameter function. If the basepoint is infinity, the diameter is equal to - the limit of the radii of the corresponding disks in ``Cp``. + the limit of the radii of the corresponding disks in `\CC_p`. If the basepoint is not infinity, the diameter is the Hsia kernel of this point with itself at @@ -1541,44 +1551,49 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" Element class of the Berkovich projective line over `\CC_p`. - Elements are categorized into four Types, which are represented as follows: + Elements are categorized into four types, represented by specific data: - - Type I points are represented by a center in projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p`. + - Type I points are represented by a center in the ``base`` of the parent Berkovich space, + which is projective space of dimension 1 over either `\QQ_p`, a finite extension of `\QQ_p`, + or a number field. - - Type II points are represented by a center in projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` and a rational power of `p`. - Type II points cannot be centered at the point at infinity. + - Type II points are represented by a center in the ``base`` of the parent Berkovich space, + and a rational power of `p`. - - Type III points are represented by a center in projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` and a radius in `[0,\infty)`. - Type III points are cannot be centered at the point at infinity. + - Type III points are represented by a center in the ``base`` of the parent Berkovich space, + and a radius in `[0,\infty)`. - - Type IV points are represented by finite list of centers in projective Space of dimension 1 over - `\QQ_p` or over a finite extension of `\QQ_p` and by a finite list of radii in `[0,\infty)`. - None of the centers can be the point at infinity. + - Type IV points are represented by a finite list of centers in the ``base`` of the parent + Berkovich space and a finite list of radii in `[0,\infty)`. + + The projective Berkovich line is viewed as the one-point compactification of + the affine Berkovich line. The projective Berkovich line therefore contains + every point of the affine Berkovich line, along with a type I point centered + at infinity. INPUT: - - ``center`` -- For type I, II, and III points, the center of the - corresponding disk in `P^1(\CC_p)`. Must be an element of `\QQ_p`, a finite extension - of `\QQ_p`, or coerce into `\QQ_p`. For type IV points, can be a list of centers - used to approximate the point or a univariate function that computes the centers - (computation starts at 1). + - ``center`` -- For type I, II, and III points, the center of the + corresponding disk in `P^1(\CC_p)`. If the parent Berkovich space was created using a number field + `K`, then ``center`` can be an element of `P^1(K)` or `K`. Otherwise, ``center`` can be an element + of a p-adic field, or projective space of dimension 1 over a padic field. + For type IV points, can be a list of centers used to approximate the point or a + univariate function that computes the centers (computation starts at 1). - - ``radius`` -- (optional) For type I, II, and III points, the radius of the - corresponding disk in ``Cp``. Must coerce into the real numbers. For type IV points, + - ``radius`` -- (optional) For type I, II, and III points, the radius of the + corresponding disk in `\CC_p`. Must coerce into the real numbers. For type IV points, can be a list of radii used to approximate the point or a univariate function that computes the radii (computation starts at 1). - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies - the power of ``p`` such that p^ ``power`` = radius. + the power of ``p`` such that `p^\text{power}` = radius - - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point. + - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point - ``error_check`` -- (default: True) If error checking should be run on input. If input is correctly formatted, can be set to ``False`` for better performance. - WARNING: Setting error_check to ``False`` can lead to incorrect results. + WARNING: with error check set to ``False``, any error in the input will lead to + incorrect results. EXAMPLES: @@ -1767,16 +1782,16 @@ def __hash__(self): return hash(str(self.radius())) def partial_order(self,other): - """ + r""" The standard partial order on Berkovich space. Roughly, the partial order corresponds to containment of the corresponding disks in ``Cp``. For example, let x and y be points of type II or III. - If x has center ``c1`` and radius ``r1`` and y has center - ``c2`` and radius ``r2``, x < y if and only if D(c1,r1) - is a subset of D(c2,r2) in ``Cp``. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2)` in `\CC_p`. INPUT: @@ -1866,19 +1881,19 @@ def partial_order(self,other): return False return None - def join(self, other, basepoint="infty"): + def join(self, other, basepoint=Infinity): """ Computes the join of this point and ``other``, with respect to ``basepoint``. The join is first point that lies on the interesection - of the path from self to basepoint and the path from other to - basepoint. + of the path from this point to ``basepoint`` and the path from ``other`` to + ``basepoint``. INPUT: - ``other`` -- A point of the same Berkovich space as this point - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point or the string 'infty' + Berkovich space as this point, or Infinity OUTPUT: A point of the same Berkovich space @@ -1961,7 +1976,7 @@ def join(self, other, basepoint="infty"): infty = parent((1,0)) - if basepoint == "infty" or basepoint == infty: + if basepoint == Infinity or basepoint == infty: if self == infty or other == infty: return infty dist = self._custom_abs(self.center()[0] - other.center()[0]) @@ -2041,11 +2056,11 @@ def join(self, other, basepoint="infty"): return other.join(self,basepoint) def involution_map(self): - """ + r""" Returns the image of this point under the involution map. The involution map is the extension of the map ``z |-> 1/z`` map - on projective space over ``Cp`` to Berkovich space. + on `P^1(\CC_p)` to Berkovich space. OUTPUT: A point of the same Berkovich space @@ -2180,7 +2195,7 @@ def potential_kernel(self, other, basepoint): - ``other`` -- A point of the same Berkovich space as this point - ``basepoint`` -- A point of the same Berkovich space as this point - OUTPUT: A real number or the infinity symbol 'oo' + OUTPUT: A finite or infinite real number EXAMPLES:: @@ -2242,11 +2257,11 @@ def spherical_kernel(self,other): return (self.prime())**(-1*dist) def diameter(self, basepoint=Infinity): - """ + r""" Generalized diameter function. If the basepoint is infinity, the diameter is equal to - the limit of the radii of the corresponding disks in ``Cp``. + the limit of the radii of the corresponding disks in `\CC_p`. If the basepoint is not infinity, the diameter is the Hsia kernel of this point with itself at @@ -2370,7 +2385,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): The Berkovich Affine line over `\CC_p`. The Berkovich Affine line is the set of seminorms on `\CC_p[x]`, - with the weakest topology that makes the map `| \cdot | \to |f|` continuous + with the weakest topology such that the map `| \cdot | \to |f|` continuous for all `f \in \CC_p[x]`. INPUT: @@ -2461,20 +2476,6 @@ def __init__(self, base, ideal=None): self._p = prime Parent.__init__(self, base=base, category=TopologicalSpaces()) - def residue_characteristic(self): - """ - The residue characteristic of the ``base``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B.residue_characteristic() - 3 - """ - return self._p - - prime = residue_characteristic - def _repr_(self): """ String representation of this Berkovich Space. @@ -2617,10 +2618,9 @@ def __init__(self, base, ideal=None): def base_ring(self): r""" - The base of this Berkovich Space. + The base ring of this Berkovich Space. - OUTPUT: A projective Space of dimension 1 over `\QQ_p` - or a finite extension. + OUTPUT: A field EXAMPLES:: @@ -2633,6 +2633,15 @@ def base_ring(self): sage: C = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3,1),1)) sage: C.base_ring() 3-adic Field with capped relative precision 1 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.prime_above(3) + sage: D = Berkovich_Cp_Projective(A, ideal) + sage: D.base_ring() + Number Field in a with defining polynomial x^3 + 20 """ return self.base().base_ring() From f5687a5e3a9351fdd1a4138ff43dc5c832df1dca Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 15 Jul 2020 15:30:48 -0400 Subject: [PATCH 057/379] 29844: removed files added by accident --- .../arithmetic_dynamics/berkovich_ds.py | 692 ------------------ 1 file changed, 692 deletions(-) delete mode 100644 src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py diff --git a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py b/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py deleted file mode 100644 index b0d7319ae7e..00000000000 --- a/src/sage/dynamics/arithmetic_dynamics/berkovich_ds.py +++ /dev/null @@ -1,692 +0,0 @@ -r""" -Dynamical systmes on Berkovich space over `\CC_p`. - -A dynamical system on Berkovich space over `\CC_p` is -determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, -which naturally induces a dynamical system on affine or -projective Berkovich space. -""" - -#***************************************************************************** -# 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.element import Element -from sage.dynamics.arithmetic_dynamics.generic_ds import DynamicalSystem -from six import add_metaclass -from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.misc.classcall_metaclass import typecall -from sage.schemes.generic.morphism import SchemeMorphism_polynomial -from sage.rings.padics.generic_nodes import is_pAdicField -from sage.schemes.berkovich.berkovich_space import (Berkovich_Cp_Affine, - Berkovich_Cp_Projective, is_Berkovich_Cp, - Berkovich_Element_Cp_Affine, Berkovich_Element_Cp_Projective) -from sage.rings.padics.factory import Qp -from sage.structure.element import get_coercion_model -from sage.schemes.projective.projective_space import ProjectiveSpace -from sage.schemes.affine.affine_space import is_AffineSpace -from sage.rings.padics.padic_base_generic import pAdicBaseGeneric -from sage.dynamics.arithmetic_dynamics.projective_ds import DynamicalSystem_projective -from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine -from sage.rings.rational_field import QQ - -@add_metaclass(InheritComparisonClasscallMetaclass) -class DynamicalSystem_Berkovich(Element): - r""" - A dynamical system on Berkovich space over `\CC_p`. - - A dynamical system on Berkovich space over `\CC_p` is - determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, - which naturally induces a dynamical system on affine or - projective Berkovich space. - - INPUT:: - - - ``dynamical_system`` -- any input which defines a dynamical - system over affine or projective space, or a dynamical system - over affine or projective space. If the input is a list - of homogenous polynomials, then ``domain`` is taken to - be projective Berkovich space, unless specified. - If this input is not defined over a padic field, - then ``domain`` MUST be specified. - - - ``domain`` -- (optional) affine or projective Berkovich space - over `\CC_p`. If the input to ``dynamical_system`` is - not defined over `\QQ_p` or a finite extension, ``domain`` - must be specified. - - EXAMPLES: - - We can easily create a dynamical system on Berkovich space - using a dynamical system on projective space over `\QQ_p`:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([2*x^2 + 4*y^2, 3*x^2 + 9*y^2]) - sage: DynamicalSystem_Berkovich(f) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - ((2 + O(3^20))*x^2 + (1 + 3 + O(3^20))*y^2 : (3 + O(3^21))*x^2 + (3^2 + O(3^22))*y^2) - - Or from a morphism:: - - sage: P1. = ProjectiveSpace(Qp(3),1) - sage: H = End(P1) - sage: DynamicalSystem_Berkovich(H([y, x])) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - (y : x) - - Or from polynomials:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: DynamicalSystem_Berkovich([x^2+y^2, y^2]) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - (x^2 + y^2 : y^2) - - Note that the default behavior on polynomial input is to construct - a dynamical system on the projective line. To construct a dynamical - system on on the affine line, specify ``domain``:: - - sage: A. = AffineSpace(Qp(3),1) - sage: B = Berkovich_Cp_Affine(3) - sage: DynamicalSystem_Berkovich([x^2+1], B) - Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x) to - (x^2 + 1 + O(3^20)) - - ``domain`` is ignored if a dynamical system or an endomorphism is - passsed in, unless that morphism is not defined over a padic ring/field:: - - sage: f = DynamicalSystem_affine(x^2+1) - sage: C = Berkovich_Cp_Projective(3) - sage: DynamicalSystem_Berkovich(f, C) - Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x) to - (x^2 + 1 + O(3^20)) - - Creating a map on Berkovich space creates the Berkovich space - it acts on:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2, y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: B = g.domain(); B - Projective Berkovich line over Cp(3) of precision 20 - - We can take the image of points of the domain:: - - sage: Q1 = B(2) - sage: g(Q1) - Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) - """ - - @staticmethod - def __classcall_private__(cls, dynamical_system, domain=None): - """ - Returns the appropriate dynamical system on Berkovich space. - - EXAMPLES:: - - sage: R. = QQ[] - sage: B = Berkovich_Cp_Affine(3) - sage: DynamicalSystem_Berkovich(t^2 - 3,B) - Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (t) to - (t^2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + 2*3^14 + 2*3^15 + 2*3^16 + 2*3^17 + 2*3^18 + 2*3^19 + 2*3^20 + O(3^21)) - """ - if not(domain is None or is_Berkovich_Cp(domain)): - raise ValueError('domain must be a Berkovich space over Cp') - morphism_domain = None - if isinstance(dynamical_system, SchemeMorphism_polynomial): - morphism_domain = dynamical_system.domain() - - if not domain is None: - if isinstance(domain, Berkovich_Cp_Affine): - return DynamicalSystem_Berkovich_affine(dynamical_system,domain) - if not morphism_domain is None: - from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine - if is_AffineSpace(morphism_domain) or isinstance(domain, AlgebraicScheme_subscheme_affine): - return DynamicalSystem_Berkovich_affine(dynamical_system,domain) - - return DynamicalSystem_Berkovich_projective(dynamical_system,domain) - - def __init__(self, dynamical_system, domain): - r""" - The Python constructor - - TESTS:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([2*x^2 + 4*y^2, 3*x^2 + 9*y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: isinstance(g, DynamicalSystem_Berkovich) - True - """ - self._system = dynamical_system - self._polys = dynamical_system._polys - self._domain = domain - - def domain(self): - """ - Returns the domain of this dynamical system. - - OUTPUT: A Berkovich space over ``Cp`` - - EXAMPLES:: - - sage: Q. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([3*x^2,2*y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: g.domain() - Projective Berkovich line over Cp(3) of precision 20 - """ - return self._domain - - def __getitem(self, i): - """ - Returns the ith polynomial. - - INPUT: - - - ``i`` -- an integer - - OUTPUT: - - - element of polynomial ring or a fraction field of a polynomial ring - - EXAMPLES:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2 + y^2, 2*y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: g[0] - x^2 + y^2 - """ - return self._polys[i] - - def defining_polynomials(self): - """ - Return the defining polynomials. - - OUTPUT: - - A tuple of polynomials that defines the - dynamical system. - - EXAMPLES:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([2*x^2 + 4*y^2, 3*x^2 + 9*y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: g.defining_polynomials() - ((2 + O(3^20))*x^2 + (1 + 3 + O(3^20))*y^2, - (3 + O(3^21))*x^2 + (3^2 + O(3^22))*y^2) - """ - return self._polys - - def _repr_(self): - r""" - Return a string representation of this dynamical system. - - OUTPUT: a string - - EXAMPLES:: - - sage: Q. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([3*x^2,2*y^2]) - sage: f = DynamicalSystem_Berkovich(f) - sage: f._repr_() - 'Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map\n - Defn: Defined on coordinates by sending (x : y) to\n ((3 + O(3^21))*x^2 : (2 + O(3^20))*y^2)' - """ - domain_str = self._domain._repr_() - return "Dynamical system of " + domain_str + " induced by the map" + \ - "\n Defn: %s"%('\n '.join(self._system._repr_defn().split('\n'))) - -class DynamicalSystem_Berkovich_projective(DynamicalSystem_Berkovich): - r""" - A dynamical system on projective Berkovich space over `\CC_p`. - - A dynamical system on projective Berkovich space over `\CC_p` is - determined by a dynamical system on `A^1(\CC_p)` or `P^1(\CC_p)`, - which naturally induces a dynamical system on affine or - projective Berkovich space. - - INPUT:: - - - ``dynamical_system`` -- any input which defines a dynamical - system over affine or projective space, or a dynamical system - over affine or projective space. If the input is a list - of homogenous polynomials, then ``domain`` is taken to - be projective Berkovich space, unless specified. - If this input is not defined over a padic field, - then ``domain`` MUST be specified. - - - ``domain`` -- (optional) affine or projective Berkovich space - over `\CC_p`. If the input to ``dynamical_system`` is - not defined over `\QQ_p` or a finite extension, ``domain`` - must be specified. - - EXAMPLES: - - We can easily create a dynamical system on Berkovich space - using a dynamical system on projective space over `\QQ_p`:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([1/2*x^2 + x*y + 3*y^2, 3*x^2 + 9*y^2]) - sage: DynamicalSystem_Berkovich_projective(f) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - ((2 + 3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20))*x^2 + x*y + (3 + O(3^21))*y^2 : (3 + O(3^21))*x^2 + (3^2 + O(3^22))*y^2) - - Or from a morphism:: - - sage: P1. = ProjectiveSpace(Qp(3),1) - sage: H = End(P1) - sage: DynamicalSystem_Berkovich_projective(H([y, x])) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - (y : x) - - Or from polynomials:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: DynamicalSystem_Berkovich_projective([x^2+y^2, y^2]) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - (x^2 + y^2 : y^2) - - Creating a map on Berkovich space creates the Berkovich space - it acts on:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2, y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: B = g.domain(); B - Projective Berkovich line over Cp(3) of precision 20 - - We can take the image of points of the domain:: - - sage: Q1 = B(2) - sage: g(Q1) - Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) - """ - @staticmethod - def __classcall_private__(cls, dynamical_system, domain=None): - """ - Returns the approapriate dynamical system on projective Berkovich space over ``Cp``. - """ - if not isinstance(dynamical_system, DynamicalSystem): - if not isinstance(dynamical_system, DynamicalSystem_projective): - dynamical_system = DynamicalSystem_projective(dynamical_system) - else: - raise ValueError('affine dynamical system passed to projective constructor') - R = dynamical_system.base_ring() - morphism_domain = dynamical_system.domain() - if not isinstance(R, pAdicBaseGeneric): - if domain is None: - raise ValueError('dynamical system not defined over padic field and domain is None') - try: - #TODO change to Qpbar - dynamical_system = dynamical_system.change_ring(Qp(domain.prime())) - morphism_domain = dynamical_system.domain() - R = dynamical_system.base_ring() - flag = False - except: - flag = True - if flag: - raise ValueError('dynamical_system could not be converted to Qp(%s)' %domain.prime()) - if morphism_domain != morphism_domain.ambient_space(): - raise ValueError('morphism must be defined on the ambient space') - if morphism_domain.dimension_absolute() != 1: - raise ValueError('domain not dimension 1') - domain = Berkovich_Cp_Projective(ProjectiveSpace(R, 1)) - return typecall(cls,dynamical_system,domain) - - def __init__(self, dynamical_system, domain=None): - """ - Python constructor. - """ - DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) - - """ - def scale_by(self, t): - "" - Scales each coordinate by a factor of ``t``. - - INPUT: - - - ``t`` -- a ring element. - - OUTPUT: - - - None. - "" - field = self.domain().base_ring() - R = field['z'] - x,y = self._polys[0].variables()[0], self._polys[0].variables()[1] - z = R.gens()[0] - old_polys = self._polys[:] - for i in range(len(old_polys)): - old_polys[i] = old_polys[i].subs({x:z,}) - new_polys = [] - for i in self: - new_polys.append(i*t.numerator()) - """ - - def conjugate(self, M, adjugate=False): - r""" - Conjugate this dynamical system by ``M``, i.e. `M^{-1} \circ f \circ M`. - - If possible the new map will be defined over the same space. - Otherwise, will try to coerce to the base ring of ``M``. - - INPUT: - - - ``M`` -- a square invertible matrix - - - ``adjugate`` -- (default: ``False``) boolean, also classically - called adjoint, takes a square matrix ``M`` and finds the transpose - of its cofactor matrix. Used for conjugation in place of inverse - when specified ``'True'``. Functionality is the same in projective space. - - OUTPUT: a dynamical system - - EXAMPLES:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2 + y^2, 2*y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: g.conjugate(Matrix([[1,1],[0,1]])) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x : y) to - (x^2 + (2 + O(3^20))*x*y : (2 + O(3^20))*y^2) - """ - return DynamicalSystem_Berkovich(self._system.conjugate(M,adjugate)) - - def dehomogenize(self, n): - """ - Returns the map induced by the standard dehomogenization. - - The dehomogenization is done at the ``n[0]`` coordinate - of the domain and the ``n[1]`` coordinate of the codomain. - - INPUT: - - - ``n`` -- a tuple of nonnegative integers; if ``n`` is an integer, - then the two values of the tuple are assumed to be the same - - OUTPUT: A dynamical system on affine Berkovich space - - EXAMPLES:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2 + y^2, x*y + y^2]) - sage: g = DynamicalSystem_Berkovich(f) - sage: g.dehomogenize(1) - Dynamical system of Affine Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x) to - ((x^2 + 1 + O(3^20))/(x + 1 + O(3^20))) - """ - new_system = self._system.dehomogenize(n) - base_ring = self.domain().base_ring().base_ring() #2 base rings since this is projective berkovich space - new_domain = Berkovich_Cp_Affine(base_ring) - return DynamicalSystem_Berkovich_affine(new_system,new_domain) - - def __call__(self, x): - """ - Makes dynamical systems on Berkovich space over ``Cp`` callable. - - INPUT: - - - ``x`` -- a point of Berkovich space over ``Cp`` - - EXAMPLES: - - The image of Type I point is the image of the center:: - - sage: P. = ProjectiveSpace(Qp(3),1) - sage: f = DynamicalSystem_projective([x^2, y^2]) - sage: F = DynamicalSystem_Berkovich(f) - sage: B = F.domain() - sage: Q1 = B(2) - sage: F(Q1) - Type I point centered at (1 + 3 + O(3^20) : 1 + O(3^20)) - - For Type II/III points with no poles in the corresponding disk, - the image is the Type II/III point corresponding to the image - of the disk:: - - sage: Q2 = B(1,3) - sage: F(Q2) - Type II point centered at (0 : 1 + O(3^20)) of radius 3^2 - - The image of Type II points can be computed even when there - are poles in the disk:: - - sage: g = DynamicalSystem_projective([x^2 + y^2, x*y]) - sage: G = DynamicalSystem_Berkovich(f) - sage: Q3 = B(0,1) - sage: G(Q3) - Type II point centered at (0 : 1 + O(3^20)) of radius 3^0 - """ - if not isinstance(x.parent(), type(self._domain)): - try: - x = self.domain()(x) - except: - raise ValueError('action of dynamical system not defined on %s' %x.parent()) - if x.type_of_point() == 1: - return self.domain()(self._system(x.center())) - if x.type_of_point() == 4: - raise NotImplementedError('action on Type IV points not implemented') - f = self._system - #for Type II points, we use the approach outlined in example 7.37 in - #'Dynamics in One Non-Archimedean Variable' by Benedetto - if x.type_of_point() == 2: - from sage.matrix.constructor import Matrix - from sage.modules.free_module_element import vector - y,w = f.domain().gens()[0],f.domain().gens()[1] - field = f.domain().base_ring() - M = Matrix([[field(x.prime()**(-1*x.power())),x.center()[0]],[field(0),field(1)]]) - X = M * vector(f[0].parent().gens()) - F = vector(f._polys) - F = list(F(list(X))) - R = field['z'] - z = R.gens()[0] - for i in range(len(F)): - F[i] = F[i].subs({y:z,w:1}) - lcm = field(1) - for poly in F: - for i in poly: - if i != 0: - lcm = i.denominator().lcm(lcm) - for i in range(len(F)): - F[i] *= lcm - gcd = [i for i in F[0] if i != 0][0] - for poly in F: - for i in poly: - if i != 0: - gcd = gcd*i*gcd.lcm(i).inverse_of_unit() - for i in range(len(F)): - F[i] *= gcd.inverse_of_unit() - gcd = F[0].gcd(F[1]) - F[0] = F[0].quo_rem(gcd)[0] - F[1] = F[1].quo_rem(gcd)[0] - fraction = [] - for poly in F: - new_poly = [] - for i in poly: - new_poly.append((i).residue()) - new_poly = R(new_poly) - fraction.append((new_poly)) - gcd = fraction[0].gcd(fraction[1]) - num = fraction[0].quo_rem(gcd)[0] - dem = fraction[1].quo_rem(gcd)[0] - if dem.is_zero(): - f = DynamicalSystem_affine(F[0]/F[1]).homogenize(1) - f = f.conjugate(Matrix([[0, 1], [1 , 0]])) - g = DynamicalSystem_Berkovich(f) - return g(self.domain()(QQ(0),QQ(1))).involution_map() - #if the reduction is not constant, the image - #is the Gauss point - if not(num.is_constant() and dem.is_constant()): - return self.domain()(QQ(0),QQ(1)) - reduced_value = field(num*dem.inverse_of_unit()).lift_to_precision(field.precision_cap()) - new_num = F[0]-reduced_value*F[1] - power_of_p = min([i.valuation() for i in new_num]) - inverse_map = (field(x.prime()**power_of_p)*z + reduced_value) - return self.domain()(inverse_map(0),(inverse_map(1)-inverse_map(0)).abs()) - #TODO write a better check for zeros in disk - P = f.domain() - if P.gens()[0] in f.defining_polynomials()[1].variables(): - raise ValueError('action on Type III points only implemented for polynomials') - nth_derivative = f.dehomogenize(1).defining_polynomials()[0] - variable = nth_derivative.parent().gens()[0] - a = x.center()[0] - Taylor_expansion = [] - from sage.functions.other import factorial - for i in range(f.degree()+1): - Taylor_expansion.append(nth_derivative(a)*1/factorial(i)) - nth_derivative = nth_derivative.derivative(variable) - r = x.radius() - new_center = f(a) - new_radius = max([Taylor_expansion[i].abs()*r**i for i in range(1,len(Taylor_expansion))]) - return self.domain()(new_center, new_radius) - -class DynamicalSystem_Berkovich_affine(DynamicalSystem_Berkovich): - r""" - A dynamical system of the affine Berkovich line over `\CC_p`. - - INPUT: - - - ``dynamical_system`` -- any input which defines a dynamical - system over affine or projective space, or a dynamical system - over affine or projective space. If the input is a list - of homogenous polynomials, then ``domain`` is taken to - be projective Berkovich space, unless specified. - If this input is not defined over a padic field, - then ``domain`` MUST be specified. - - - ``domain`` -- (optional) affine or projective Berkovich space - over `\CC_p`. If the input to ``dynamical_system`` is - not defined over `\QQ_p` or a finite extension, ``domain`` - must be specified. - - EXAMPLES: - - A dynamical system of the affine Berkovich line is - induced by a dynamical system on `\QQ_p` or an extension - of `\QQ_p`:: - - sage: A. = AffineSpace(Qp(5), 1) - sage: f = DynamicalSystem_affine([(x^2+1)/x]) - sage: DynamicalSystem_Berkovich_affine(f) - Dynamical system of Affine Berkovich line over Cp(5) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x) to - ((x^2 + 1 + O(5^20))/x) - - Dynamical systems can also be created directly from polynomials:: - - sage: DynamicalSystem_Berkovich_affine([(x^2+1)/x]) - Dynamical system of Affine Berkovich line over Cp(5) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x) to - ((x^2 + 1 + O(5^20))/x) - - Or from a morphism:: - - sage: H = End(A) - sage: phi = H([x+3]) - sage: DynamicalSystem_Berkovich_affine(phi) - Dynamical system of Affine Berkovich line over Cp(5) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x) to - (x + 3 + O(5^20)) - """ - @staticmethod - def __classcall_private__(cls, dynamical_system, domain=None): - if not isinstance(dynamical_system, DynamicalSystem_affine): - dynamical_system = DynamicalSystem_affine(dynamical_system) - R = dynamical_system.base_ring() - morphism_domain = dynamical_system.domain() - if not isinstance(R, pAdicBaseGeneric): - if domain is None: - raise ValueError('dynamical system not defined over padic field and domain is None') - try: - #TODO change to Qpbar - dynamical_system = dynamical_system.change_ring(Qp(domain.prime())) - morphism_domain = dynamical_system.domain() - R = dynamical_system.base_ring() - flag = False - except: - flag = True - if flag: - raise ValueError('dynamical_system could not be converted to Qp(%s)' %domain.prime()) - if morphism_domain != morphism_domain.ambient_space(): - raise ValueError('morphism must be defined on the ambient space') - if morphism_domain.dimension_absolute() != 1: - raise ValueError('domain not dimension 1') - domain = Berkovich_Cp_Affine(R) - return typecall(cls,dynamical_system,domain) - - def __init__(self, dynamical_system, domain): - """ - Python constructor. - """ - DynamicalSystem_Berkovich.__init__(self, dynamical_system, domain) - - def homogenize(self, n): - """ - Returns the homogenization of this dynamical system. - - For dynamical systems on Berkovich space, this is the dynamical - system on projective space induced by the homogenization of - the dynamical system. - - INPUT: - - - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, - then the two values of the tuple are assumed to be the same - - OUTPUT: a dynamical system on projective Berkovich space - - EXAMPLES:: - - sage: A. = AffineSpace(Qp(3),1) - sage: f = DynamicalSystem_affine(1/x) - sage: f = DynamicalSystem_Berkovich(f) - sage: f.homogenize(1) - Dynamical system of Projective Berkovich line over Cp(3) of precision 20 induced by the map - Defn: Defined on coordinates by sending (x0 : x1) to - (x1 : x0) - - """ - new_system = self._system.homogenize(n) - base_ring = self.domain().base_ring() - new_domain = Berkovich_Cp_Affine(base_ring) - return DynamicalSystem_Berkovich_projective(new_system,new_domain) - - def __call__(self, x): - """ - Makes this dynamical system callable. - - EXAMPLES:: - - sage: P. = AffineSpace(Qp(3),1) - sage: f = DynamicalSystem_affine(x^2) - sage: g = DynamicalSystem_Berkovich(f) - sage: B = g.domain() - sage: Q1 = B(2) - sage: g(Q1) - Type I point centered at 1 + 3 + O(3^20) - """ - if not isinstance(x, Berkovich_Element_Cp_Affine): - try: - x = self.domain()(x) - except: - raise ValueError('action of dynamical system not defined on %s' %x) - proj_system = self.homogenize(1) - return self.domain()(proj_system(x)) \ No newline at end of file From b31670320812a33f6d135734f961e68ecf78734e Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 15 Jul 2020 15:32:38 -0400 Subject: [PATCH 058/379] 29844: fixed import statements --- src/sage/dynamics/arithmetic_dynamics/all.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/all.py b/src/sage/dynamics/arithmetic_dynamics/all.py index 5686ab3ba48..b434f8001bc 100644 --- a/src/sage/dynamics/arithmetic_dynamics/all.py +++ b/src/sage/dynamics/arithmetic_dynamics/all.py @@ -5,7 +5,5 @@ from .affine_ds import DynamicalSystem_affine from .projective_ds import DynamicalSystem_projective from .product_projective_ds import DynamicalSystem_product_projective -from .berkovich_ds import (DynamicalSystem_Berkovich, DynamicalSystem_Berkovich_affine, - DynamicalSystem_Berkovich_projective) lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'WehlerK3Surface') lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'random_WehlerK3Surface') From 06b8d25f4eea078ec300f92090c4e34aa61a987c Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 16 Jun 2020 15:09:19 +0100 Subject: [PATCH 059/379] code for symmetric matrices --- src/sage/matrix/matrix_space.py | 113 ++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index a8fc5e64493..12ae9bfa140 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -1463,6 +1463,119 @@ def __iter__(self): for iv in sage.combinat.integer_vector.IntegerVectors(weight, number_of_entries, max_part=(order-1)): yield self(entries=[base_elements[i] for i in iv]) + + def symmetric_matrices(self, f, g=None ): + r""" + Return a generator of the matrices in this matrix space that satisfy: + `A[j,i] = f(A[i,j])` for `i != j` + `A[i,i] = g(A[i,i])` + + If the matrix space doesn't contains square matrices, then a + `ValueError` is raised. + + INPUT: + + - `f` -- function + + - `g` -- (optional) funcition; if it is None, then we assume `g=f`, + default value: `None`. + + EXAMPLES: + + + TESTS:: + + """ + if self.__nrows != self.__ncols: + raise ValueError("can't have symmetric matrices if they are not square") + if g == None: + g = f + + def make_symmetric( M ): + for i in range(M.nrows()): + for j in range(i+1, M.nrows()): + M[j,i] = f(M[i,j]) + + #Make sure that we can iterate over the base ring + base_ring = self.base_ring() + base_iter = iter(base_ring) + + nrows = self.__nrows + number_of_entries = nrows**2 + entries_in_upper_half = (nrows*(nrows-1))//2 + + #If the number of entries is zero, then just + #yield the empty matrix in that case and return + if number_of_entries == 0: + yield self(0) + return + + import sage.combinat.integer_vector + + if not base_ring.is_finite(): + #When the base ring is not finite, then we should go + #through and yield the matrices by "weight", which is + #the total number of iterations that need to be done + #on the base ring to reach the matrix. + base_elements = [ next(base_iter) ] + weight = 0 + while True: + for iv in sage.combinat.integer_vector.IntegerVectors(weight, entries_in_upper_half+nrows): + #Now we need to contruct the entries of the matrix so that is symmetric + #with respect to f and g + matrix_entries = [] #the upper half (with diagonal) entries of the matrix + length_of_row = nrows # == self.__ncols + valid_diagonal = True#if false, then we need a new integer vector + for _ in range(nrows): + #check diagonal + if base_elements[iv[0]] != g( base_elements[iv[0]] ): + valid_diagonal = False + break + #now construct the row + zeros = [0]*(nrows - length_of_row) + row = zeros + [base_elements[i] for i in iv[:length_of_row]] + matrix_entries.extend(row) #append row + + iv = iv[length_of_row:] + length_of_row -= 1 + + if valid_diagonal: + M = self(entries=matrix_entries) + #now make M symmetric with respect to f + make_symmetric(M) + yield M + #else iterate + weight += 1 + base_elements.append( next(base_iter) ) + else: + #In the finite case, we do a similar thing except that + #instead of checking if the diagonal is correct after creating the vector + #we can select all possible diagonal elements a priori + order = base_ring.order() + base_elements = list(base_ring) + diagonal_elements = [ x for x in base_elements if g(x) == x ] + number_diagonal_elements = len(diagonal_elements) + for weight1 in range((order-1)*entries_in_upper_half+1): + for iv2 in sage.combinat.integer_vector.IntegerVectors(weight1, entries_in_upper_half, max_part=(order-1)): + for weight2 in range((number_diagonal_elements-1)*nrows+1): + for dia in sage.combinat.integer_vector.IntegerVectors(weight2, nrows, max_part=(number_diagonal_elements-1)): + iv = iv2.clone() # iv is going to be changed within the next loop, so we keep a copy iv2 + #construct upper half matrix + matrix_entries = [] #entries of matrix with lower half 0 + length_of_row = nrows-1 + for r in range(nrows): + zeros = [0]*(nrows - length_of_row - 1) + row = zeros + [diagonal_elements[dia[r]]] + [base_elements[i] for i in iv[:length_of_row]] + matrix_entries.extend(row) + + iv = iv[length_of_row:] + length_of_row -= 1 + + M = self(entries=matrix_entries) + #make M symmetric + make_symmetric(M) + yield M + def __getitem__(self, x): """ Return a polynomial ring over this ring or the `n`-th element of this ring. From 9991f4f1c4ccf49bb1071a9d957e26db3dfc1d1d Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 16 Jun 2020 15:22:20 +0100 Subject: [PATCH 060/379] added file --- src/sage/combinat/designs/gen_quadrangles.pxd | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/sage/combinat/designs/gen_quadrangles.pxd diff --git a/src/sage/combinat/designs/gen_quadrangles.pxd b/src/sage/combinat/designs/gen_quadrangles.pxd new file mode 100644 index 00000000000..7ef2637b162 --- /dev/null +++ b/src/sage/combinat/designs/gen_quadrangles.pxd @@ -0,0 +1,3 @@ + +asdas +a From 931859b3f3cce981db8ae29206e8769c70ce5ff5 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 16 Jun 2020 15:23:54 +0100 Subject: [PATCH 061/379] added code --- src/sage/combinat/designs/gen_quadrangles.pxd | 128 +++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/gen_quadrangles.pxd b/src/sage/combinat/designs/gen_quadrangles.pxd index 7ef2637b162..d91a8540e69 100644 --- a/src/sage/combinat/designs/gen_quadrangles.pxd +++ b/src/sage/combinat/designs/gen_quadrangles.pxd @@ -1,3 +1,127 @@ +def generalised_quadrangle_with_spread(const int s, const int t, existence=False, check=True): + r""" + Returns a pair (GQ,S) s.t. GQ is a generalised quadrangle of order (s,t) and S is a spread of GQ + """ + if s < 1 or t < 1: + if existence: return False + raise RuntimeError("No GQ of order ({},{}) exists".format(s,t)) -asdas -a + if s == 1 and t == 1:#we have a square + if existence: return True + D = IncidenceStructure([[0,1],[1,2],[2,3],[3,0]]) + return (D,[[0,1],[2,3]]) + + if is_prime_power(s) and t == s*s: + if existence: return True + (GQ,S) = dual_GQ_ovoid(*generalised_quadrangle_hermitian(s)) + if check: + if not is_GQ_with_spread(GQ,S,s,t): + raise RuntimeError("Sage built a wrong GQ with spread") + return (GQ,S) + + if existence: return Unknown + raise RuntimeError("Sage can't build a GQ of order ({},{}) with a spread".format(s,t)) + +def is_GQ_with_spread(GQ,S,const int s, const int t): + r""" + Checks if GQ is a generalised quadrangle of order (s,t) and + checks that S is a spred of GQ + """ + res = GQ.is_generalised_quadrangle(parameters=True) + if res is False or res[0] != s or res[1] != t: + return False + + #check spread + points = set(GQ.ground_set()) + for line in S: + if not points.issuperset(line): + return False + points = points.difference(line) + + if points: + return False + + return True + +def dual_GQ_ovoid(GQ,O): + r""" + Computes the dual of GQ and returns the image of O under the dual map + """ + #we compute the dual of GQ and of O + + #GQ.ground_set()[i] becomes newBlocks[i] + #GQ.blocks()[i] becomes i + newBlocks = [ [] for _ in range(GQ.num_points())] + pointsToInt = { p: i for i,p in enumerate(GQ.ground_set()) } + + for i,b in enumerate(GQ.blocks()): + for p in b: + newBlocks[pointsToInt[p]].append(i) + + S = [ newBlocks[pointsToInt[p]] for p in O] + + D = IncidenceStructure(newBlocks) + return (D,S) + +def generalised_quadrangle_hermitian(const int q): + r""" + Construct the generalised quadrangle H(3,q^2) with an ovoid + The GQ has order (q^2,q) + """ + + GU = libgap.GU(4,q) + H = libgap.InvariantSesquilinearForm(GU)["matrix"] + Fq = libgap.GF(q*q) + zero = libgap.Zero(Fq) + one = libgap.One(Fq) + V = libgap.FullRowSpace(Fq,4) + + e1 = [one,zero,zero,zero] #isotropic point + assert( e1*H*e1 == zero, "e1 not isotropic") + + points = list(libgap.Orbit(GU,e1,libgap.OnLines)) #all isotropic points + pointInt = { x:(i+1) for i,x in enumerate(points) } #+1 because GAP starts at 1 + #points is the hermitian variety + + GUp = libgap.Action(GU, points, libgap.OnLines)#GU as permutation group of points + + e2 = [zero,one,zero,zero] + #we have totally isotropic line + line = V.Subspace([e1,e2]) + lineAsPoints = [libgap.Elements(libgap.Basis(b))[0] for b in libgap.Elements(line.Subspaces(1)) ] + line = libgap.Set([ pointInt[p] for p in lineAsPoints ]) + + lines = libgap.Orbit(GUp, line, libgap.OnSets)#all isotropic lines + + #to find ovoid, we embed H(3,q^2) in H(4,q^2) + #then embedding is (a,b,c,d) -> (a,b,0,c,d) [so we preserve isotropicity] + #then find a point in the latter and not in the former + #this point will be collinear in H(3,q^2) to all (and only) the points in a ovoid + W = libgap.FullRowSpace(Fq,5) + J = [ [0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]] + J = libgap(J) + if q%2 == 1: + (p,k) = is_prime_power(q,get_data=True) + a = (p-1)// 2 + aGap = zero + for i in range(a): aGap += one + p = [zero,one,one,aGap,zero] + else: + a = libgap.PrimitiveRoot(Fq)**(q-1) + p = [zero,one,a+one,a,zero] + + #now p is a point of H(4,q^2) + + #p' is collinear to p iff p'Jp^q = 0 + #note that p'Jp^q = bx^q + c where p' =(a,b,0,c,d) and p=(0,1,1,x,0) + #hece we have points (0,0,0,1); (0,1,c,a) for any a iff c^q+c = 0 (c = -x^q) + #and (1,b,c,x) for any x and any b (c= -bx^q) iff it is an iso point + #so we need only q^2 (for last case) +1 (for 2nd case) checks + ovoid = [] + xq = p[3]**q + for p2 in points: + if p2[1]*xq+p2[2] == zero: #p collinear to newP2 + ovoid.append(libgap(pointInt[p2])) + + D = IncidenceStructure(lines) + return (D,ovoid) From cc924a3f7348e68f99f9e3e06fac373a8abd70e8 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Tue, 21 Jul 2020 18:35:30 +0530 Subject: [PATCH 062/379] code added --- src/sage/graphs/base/boost_graph.pyx | 123 +++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 9ad09ff59c3..c38d4c6e115 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1929,3 +1929,126 @@ cpdef radius_DHV(g, weight_function=None, check_weight=True): return +Infinity return UB + +cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, algorithm=None): + r""" + """ + import sys + from sage.graphs.generic_graph import GenericGraph + + if not isinstance(g, GenericGraph): + raise TypeError("the input must be a Sage graph") + + if not isinstance(vertex_list, list): + vertex_list = [vertex_list] + + for v in vertex_list: + if v not in g: + raise ValueError("the starting vertex " + str(v) + " is not in " + + "the graph") + + if vertex_list == None: + vertex_list = list(g) + + # These variables are automatically deleted when the function terminates. + cdef v_index vi, vert + cdef dict int_to_v = dict(enumerate(g)) + cdef dict v_to_int = {vv: vi for vi, vv in enumerate(g)} + cdef result_distances result + cdef BoostVecWeightedDiGraphU g_boost_dir + cdef BoostVecWeightedGraph g_boost_und + + if g.is_directed(): + boost_weighted_graph_from_sage_graph(&g_boost_dir, g, v_to_int, weight_function) + else: + boost_weighted_graph_from_sage_graph(&g_boost_und, g, v_to_int, weight_function) + + if algorithm is None: + # Check if there are edges with negative weights + if weight_function is not None: + for e in g.edge_iterator(): + if float(weight_function(e)) < 0: + algorithm = 'Bellman-Ford' + break + elif g.weighted(): + for _,_,w in g.edge_iterator(): + if float(w) < 0: + algorithm = 'Bellman-Ford' + break + + if algorithm is None: + algorithm = 'Dijkstra' + + try: + if weight_function is not None: + correct_type = type(weight_function(next(g.edge_iterator()))) + elif g.weighted(): + correct_type = type(next(g.edge_iterator())[2]) + else: + correct_type = int + except StopIteration: + correct_type = int + + # Needed for rational curves. + from sage.rings.real_mpfr import RealNumber, RR + if correct_type == RealNumber: + correct_type = RR + + distances = {} + predecessors = {} + + for v in vertex_list: + vi = v_to_int[v] + if g.is_directed(): + + if algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost']: + sig_on() + result = g_boost_dir.bellman_ford_shortest_paths(vi) + sig_off() + + elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: + try: + sig_on() + result = g_boost_dir.dijkstra_shortest_paths(vi) + sig_off() + if not result.distances.size(): + raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + except RuntimeError: + raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + else: + raise ValueError(f"unknown algorithm {algorithm!r}") + else: + if algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost']: + sig_on() + result = g_boost_und.bellman_ford_shortest_paths(vi) + sig_off() + + elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: + try: + sig_on() + result = g_boost_und.dijkstra_shortest_paths(vi) + sig_off() + if not result.distances.size(): + raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + except RuntimeError: + raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + else: + raise ValueError(f"unknown algorithm {algorithm!r}") + + dist_v = {} + pred_v = {} + + for vert in range(g.num_verts()): + if result.distances[vert] != sys.float_info.max: + w = int_to_v[vert] + dist_v[w] = correct_type(result.distances[vert]) + pred_v[w] = int_to_v[result.predecessors[vert]] if result.predecessors[vert] != vert else None + + distances[v] = dist_v + predecessors[v] = pred_v + + if len(vertex_list) == 1: + v = vertex_list[0] + return (distances[v], predecessors[v]) + + return (distances, predecessors) From 42b51f053b024adb0158b3d761eca82d1ebfd1b0 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 21 Jul 2020 15:49:39 -0400 Subject: [PATCH 063/379] 29844: added functions to Berkovich space, documentation fixes, added conversion functions for elements --- src/sage/schemes/berkovich/berkovich_space.py | 1042 ++++++++++------- 1 file changed, 627 insertions(+), 415 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 0d77fa47579..77f347386b4 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -33,7 +33,7 @@ from sage.structure.element import Element from sage.categories.topological_spaces import TopologicalSpaces from sage.symbolic.expression import is_Expression -from sage.rings.real_mpfr import RR +from sage.rings.real_mpfr import RR, is_RealNumber from sage.rings.padics.padic_generic_element import pAdicGenericElement from sage.rings.padics.padic_base_generic import pAdicBaseGeneric from sage.rings.padics.generic_nodes import is_pAdicField @@ -55,8 +55,8 @@ def is_Berkovich(space): OUTPUT: - - ``True`` if ``space`` is a Berkovich space - - ``False`` otherwise + - ``True`` if ``space`` is a Berkovich space. + - ``False`` otherwise. """ return isinstance(space, Berkovich) @@ -66,8 +66,8 @@ def is_Berkovich_Cp(space): OUTPUT: - - ``True`` if ``space`` is a Berkovich space over ``Cp`` - - ``False`` otherwise + - ``True`` if ``space`` is a Berkovich space over ``Cp``. + - ``False`` otherwise. """ return isinstance(space, Berkovich_Cp) @@ -92,21 +92,30 @@ class Berkovich_Element_Cp(Berkovich_Element): :: - sage: B(0,1) + sage: B(0, 1) Type II point centered at 0 of radius 3^0 """ - def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, error_check=True): + def __init__(self, parent, center, radius=None, power=None, prec=20, space_type=None, error_check=True): + """ + Initialization function. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(5) + sage: B(4) + Type I point centered at 4 + O(5^20) + """ from sage.rings.function_field.element import is_FunctionFieldElement from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.fraction_field_element import FractionFieldElement_1poly_field self._type = None - #if radius is a list, this is a type 4 point - if isinstance(radius, list): + #if radius is a list or a tuple, this is a type 4 point + if isinstance(radius, list) or isinstance(radius, tuple): if error_check: - if not isinstance(center, list): - raise ValueError("center was passed a list but radius was not a list") + if not (isinstance(center, list) or isinstance(center, tuple)): + raise TypeError("center was passed a list but radius was not a list") if len(radius) != len(center): raise ValueError("the same number of centers and radii must be specified to create " + \ "a type IV point") @@ -116,14 +125,27 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, self._center_func = None self._radius_func = None self._type = 4 + self._radius = None + self._center = None if not error_check: return #is_FunctionFieldElement calls .parent - elif hasattr(center, "parent"): - #if center is a supported univariate function, this is a type 4 point - if is_FunctionFieldElement(center) or is_Polynomial(center) or\ - isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center): + elif hasattr(center, "parent") and hasattr(radius, 'parent'): + from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial + if is_MPolynomial(center): + try: + center = center.univariate_polynomial() + except: + raise TypeError('center was %s, a multivariable polynomial' %center) + + #check if the radius and the center are functions + center_func_check = is_FunctionFieldElement(center) or is_Polynomial(center) or\ + isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center) + radius_func_check = is_FunctionFieldElement(radius) or is_Polynomial(radius) or\ + isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius) + + if center_func_check: #check that both center and radii are supported univariate function center_expr_check = False radius_expr_check = False @@ -133,11 +155,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, raise ValueError("an expression with %s " %(len(center.variables())) + \ "variables cannot define the centers approximating a type IV point") else: - #we do this since .subs is currently bugged for polynomials but not expressions + #we do this since .subs is currently buggy for polynomials but not expressions center_expr_check = True - if not (is_FunctionFieldElement(radius) or is_Polynomial(radius) or\ - isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius)): - raise ValueError("center was passed a function but radius was not a function") + if not radius_func_check: + raise TypeError("center was passed a function but radius was not a function") if is_Expression(radius): if len(radius.variables()) != 1: raise ValueError("an expression with %s " %(len(radius.variables())) + \ @@ -164,7 +185,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, #we use .subs for expressions to avoid deprecation center_lst.append(self._center_func.subs({x:i})) else: - #.subs for polynomials is currently bugged + #.subs for polynomials is currently buggy center_lst.append(self._center_func(i)) if radius_expr_check: radius_lst.append(self._radius_func.subs({y:i})) @@ -172,12 +193,13 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, radius_lst.append(self._radius_func(i)) self._center_lst = center_lst self._radius_lst = radius_lst + self._radius = None + self._center = None if not error_check: return if self._type == 4 and error_check: - from sage.rings.real_mpfr import is_RealNumber - if child == "projective": + if space_type == "projective": for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] @@ -185,52 +207,44 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) - self._center_lst[i] = center - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of projective Berkovich" + \ - "space must be a point of P^1(Cp(%s)), not of %s"%(self._p, center.parent())) + except (TypeError, ValueError) as e: + raise TypeError('could not convert %s to %s' %(center, self._base_space)) + if self._base_type == 'padic field': + if not is_pAdicField(center.scheme().base_ring()): + if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError("could not convert %s to %s" %(center, self._base_space)) + else: + # center is padic, not but an element of a scheme over a padic field. + # we convert to scheme over a padic field + center = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1)(center) + if center.scheme().base_ring().prime() != self._p: + raise ValueError("center must be an element of " + \ + "%s not %s" %self._base_space, center.scheme()) else: - if self._base_type == 'padic field': - if not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(),pAdicBaseGeneric): - try: - center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s") %self._base_space - else: - try: - center = (self._base_space)(center) - except: - pass - if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("the center of a disk approximating a type IV point of Berkovich space" + \ - " over P^1(Cp(%s)) cannot be a point of %s") %(self._p, center.scheme()) - else: - if center not in self._base_space: - raise ValueError('center of a point of Berkovich space in projective space ' + \ - 'over the wrong field') + if center not in self._base_space: + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) if center.scheme().ambient_space() != center.scheme(): raise ValueError("the center of a point of Berkovich space over " + \ - "P^1(Cp(%s)) cannot be a point of %s" %(self._p,center.scheme())) + "P^1(Cp(%s)) must be a point of Cp not %s" %(self._p,center.scheme())) if center == (center.scheme())((1,0)): raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ "space cannot be centered at %s" %((center.scheme())((1,0)))) + #since we are over a field, we can normalize coordinates. all code assumes normalized coordinates + center.normalize_coordinates() #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): radius = RR(radius) elif RR.has_coerce_map_from(radius.parent()): radius = RR(radius) - self._radius_lst[i] = radius else: - raise ValueError("the radius of a disk approximating a type IV point" + \ + raise TypeError("the radius of a disk approximating a type IV point" + \ "must coerce into the real numbers, %s does not coerce" %(radius)) if i != 0: #check containment for the sequence of disks @@ -240,8 +254,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if previous_radius < radius or dist > radius: raise ValueError("sequence of disks does not define a type IV point as" + \ "containment is not proper") + self._center_lst[i] = center + self._radius_lst[i] = radius return - elif child == "affine": + elif space_type == "affine": for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] @@ -250,26 +266,20 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) - self._center_lst[i] = center - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a disk approximating a type IV point must " + \ - "be padic or coerce into Qp, %s does not coerse into Qp" %(center)) + except (TypeError, ValueError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) elif not is_pAdicField(center.parent()): - try: - center = (self._base_space)(center) - except: - pass + #center is padic, not but an element of a padic field. we convert to padic field + center = (center.parent().fraction_field())(center) if (center.parent()).prime() != self._p: - raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ - "space over Cp(%s) cannot be a point of %s") %(self._p, center.parent()) + raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) else: #make sure the center is in the appropriate number field if not(center in self._base_space): - raise ValueError('center defined over %s passed to ' %center.parent()+ \ - 'Berkovich space defined over number field %s' %self._base_space) + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) #make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): @@ -288,106 +298,100 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if previous_radius < radius or dist > radius: raise ValueError("sequence of disks does not define a type IV point as " + \ "containment is not proper") + self._center_lst[i] = center + self._radius_lst[i] = radius return else: - raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ + raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ "Berkovich_Element_Cp directly" ) return #the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type if error_check: - if child == 'affine': - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of affine Berkovich space over " + \ - "%s must convert to %s" %(self._base_space,self._base_space)) + if space_type == 'affine': if self._base_type == 'padic field': - if not is_pAdicField(center.parent()): + #make sure the center is in Cp + if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) - except: - pass + except (TypeError, ValueError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) + elif not is_pAdicField(center.parent()): + #center is padic, not but an element of a padic field. we convert to padic field + center = (center.parent().fraction_field())(center) if (center.parent()).prime() != self._p: - raise ValueError("the center of a point of Berkovich space over " + \ - "Cp(%s) cannot be a point of %s" %(self._p, center.parent())) + raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) else: + #make sure the center is in the appropriate number field if not(center in self._base_space): - raise ValueError('center defined over %s passed to ' %center.parent()+ \ - 'Berkovich space defined over number field %s' %self._base_space) - elif child == "projective": + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) + elif space_type == "projective": if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - if self._base_type == 'padic field': - raise ValueError("the center of a point of projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s" %self._base_space) - else: - raise ValueError('the center of a point of projective Berkovich '+ \ - 'space over %s must be coerce into %s' %(self._base_space, self._base_space)) + except (ValueError, TypeError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: center = (self._base_space)(center) - flag = False - except: - flag = True - if flag: - raise ValueError("the center of a point of projective Berkovich space must be a " + \ - "point of P^1(Cp) or coerce into %s") %self._base_space + except (TypeError, ValueError) as e: + raise ValueError("could not convert %s to %s" %(center, self._base_space)) else: + # center is padic, not but an element of a scheme over a padic field. + # we convert to scheme over a padic field + field_scheme = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1) try: - center = (self._base_space)(center) - except: - pass - if (center.scheme()).base_ring().prime() != self._p: - raise ValueError("the center of a point of Berkovich space over " + \ - "P^1(Cp(%s)) cannot be a point of %s" %(self._p, center.scheme())) + center = field_scheme(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %center, field_scheme) + if center.scheme().base_ring().prime() != self._p: + raise ValueError("center must be an element of " + \ + "%s not %s" %self._base_space, center.scheme()) else: - if not(center in self._base_space): - raise ValueError('center defined over %s passed to ' %center.parent()+ \ - 'Berkovich space defined over number field %s' %self._base_space) + if center not in self._base_space: + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) if not(center.scheme().ambient_space() is center.scheme()): raise ValueError("the center of a point of projective Berkovich space cannot be " + \ "a point of %s" %(center.scheme())) + #since we are over a field, we normalize coordinates + center.normalize_coordinates() else: - raise ValueError("bad value %s passed to child. Do not initialize "%(child) + \ + raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ "Berkovich_Element_Cp directly") - if self._base_type == 'number field': - if not(center in self._base_space): - raise ValueError('center defined over %s passed to ' %center.parent()+ \ - 'Berkovich space defined over number field %s' %self._base_space) + self._center = center + + #since this point is not type IV, these are None + self._center_func = None + self._center_lst = None + self._radius_lst = None + self._radius_func = None + if (radius == None and power == None) or radius == 0: self._type = 1 self._radius = 0 self._power = None return #In order to simplify our representation, type II and III points cannot be centered at infinity - if child == "projective": - try: - center.dehomogenize(1) - except ValueError: - raise ValueError("type II and III points cannot be centered at (1 : 0)") + if space_type == "projective": + #TODO use involution map to allow for infinity to be passed in as center + if center[1] == 0: + raise ValueError('type II and III points can not be centered at infinity') if power != None: if error_check: - if power.parent() != QQ: + if not(power.parent() is QQ): try: power = QQ(power) - flag = False - except: - flag = True - if flag: - raise ValueError("power must convert to rationals") + except TypeError: + raise TypeError("power must convert to rationals") if radius != None: if radius != RR(self._p**power): raise ValueError("conflicting inputs for power and radius") @@ -399,22 +403,21 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, if is_Expression(radius): try: power = QQ((radius.log(self._p)).expand_log()) - except: + except TypeError: pass try: radius = RR(radius) self._radius = radius - flag = False - except: - flag = True - if flag: - raise ValueError("symbolic radius must be a real number") - from sage.rings.real_mpfr import is_RealNumber + except TypeError: + if len(radius.variables()) == 1: + raise ValueError('radius univariate function but center is constant. ' + \ + 'this does not define a type IV point') + raise TypeError("symbolic radius must be a real number") if (not is_RealNumber(radius)) and power == None: if RR.has_coerce_map_from(radius.parent()): self._radius = RR(radius) else: - raise ValueError("radius must coerce into real numbers") + raise TypeError("radius must coerce into real numbers") else: self._radius = radius if power != None: @@ -428,6 +431,9 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, child=None, else: self._type = 3 self._power = power + return + + raise TypeError('unknown error constructing point of Berkovich space over Cp') def _custom_abs(self, x): """ @@ -438,8 +444,16 @@ def _custom_abs(self, x): EXAMPLES:: - sage: B = Berkovich_Cp_Affine(QQ,3) - sage: B(3)._custom_abs(9) + sage: B = Berkovich_Cp_Affine(QQ, 3) + sage: Q1 = B(9) + sage: Q1._custom_abs(Q1.center()) + 1/9 + + :: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(9) + sage: Q1._custom_abs(Q1.center()) 1/9 """ if self._base_type == 'padic field': @@ -450,20 +464,79 @@ def _custom_abs(self, x): return (self.prime())**(-1*x.valuation(self._ideal)/self._ideal.absolute_ramification_index()) return (self.prime())**(-1*x.valuation(self._ideal)) + def center_function(self): + """ + Returns the function defining the centers of disks in the approximation. + + Not defined unless this point is a type IV point created by using + a univariate function to compute centers. + + OUTPUT: A univariate function. + + EXAMPLES:: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q1 = B(f, g) + sage: Q1.center_function() + (1 + O(5^20))/((1 + O(5^20))*t) + """ + if self.type_of_point() != 4: + raise ValueError('center_function not defined for points which are not type IV') + if self._center_func == None: + raise ValueError('this type IV point does not have a center function') + return self._center_func + + def radius_function(self): + """ + Returns the function defining the radii of disks in the approximation. + + Not defined unless this point is a type IV point created by using + a univariate function to compute radii. + + OUTPUT: A univariate function. + + EXAMPLES:: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q1 = B(f, g) + sage: Q1.radius_function() + 40.0000000000000*pi/x + """ + if self.type_of_point() != 4: + raise ValueError('center_function not defined for points which are not type IV') + if self._radius_func == None: + raise ValueError('this type IV point does not have a radius function') + return self._radius_func + def precision(self): """ Returns the precision of a type IV point. Not defined for type I, II, or III points. - OUTPUT: An integer + OUTPUT: An integer. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) - sage: d.prec() + sage: d.precision() 3 + + TESTS:: + + sage: d.precision == d.prec + True """ if self._type in [1,2,3]: raise AttributeError("type I, II, and III points do not have a precision") @@ -480,8 +553,8 @@ def power(self): OUTPUT: - - A rational for type II points - - A real number for type III points + - A rational for type II points. + - A real number for type III points. EXAMPLES:: @@ -492,7 +565,7 @@ def power(self): :: - sage: Q2 = B(1,4) + sage: Q2 = B(1, 4) sage: Q2.power() 1.26185950714291 """ @@ -506,8 +579,8 @@ def radius(self): OUTPUT: - - A non-negative real number for points Types I-III - - A list of non-negative real numbers for type IV points + - A non-negative real number for points Types I-III. + - A list of non-negative real numbers for type IV points. EXAMPLES:: @@ -523,7 +596,7 @@ def radius(self): [1.76100000000000, 1.12300000000000, 1.11200000000000] """ if self._type == 4: - return self._radius_lst[:] + return self._radius_lst return self._radius def diameter(self): @@ -549,7 +622,7 @@ def diameter(self): :: - sage: Q2 = B(1/2,9) + sage: Q2 = B(1/2, 9) sage: Q2.diameter() 9.00000000000000 @@ -565,9 +638,9 @@ def diameter(self): """ if self._type == 4: if self._radius_func == None: - return self._radius_lst[len(self._radius_lst)-1] + return self._radius_lst[-1] from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(QQ,names="x") + R = PolynomialRing(QQ, names="x") x = R.gens()[0] if is_Expression(self._radius_func): radius_func_variable = self._radius_func.variables()[0] @@ -581,7 +654,7 @@ def diameter(self): def path_distance_metric(self, other): r""" - Returns the path distance metric distance between ``self`` and ``other``. + Returns the path distance metric distance between this point and ``other``. Also referred to as the hyperbolic metric, or the big metric. @@ -592,15 +665,15 @@ def path_distance_metric(self, other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. - OUTPUT: A finite or infinite real number + OUTPUT: A finite or infinite real number. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(1/4,4) - sage: Q2 = B(1/4,6) + sage: Q1 = B(1/4, 4) + sage: Q2 = B(1/4, 6) sage: Q1.path_distance_metric(Q2) 0.369070246428542 @@ -609,20 +682,24 @@ def path_distance_metric(self, other): sage: Q3 = B(1) sage: Q3.path_distance_metric(Q1) +infinity + + :: + + sage: Q3.path_distance_metric(Q3) + 0 """ - if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("path distance metric not defined between point of " + \ - "Berkovich space and %s" %(other)) + if not isinstance(other, type(self)): + raise TypeError('other was not a point of Berkovich space') if self.parent() != other.parent(): - raise ValueError("input was an element of a different Berkovich space") + raise ValueError("other was an element of a different Berkovich space") if self.type_of_point() == 1 or other.type_of_point() == 1: if self == other: return 0 else: return RR(Infinity) - return 2*((self.join(other)).diameter().log(self.prime()))\ - -self.diameter().log(self.prime())\ - -other.diameter().log(other.prime()) + return 2*(self.join(other).diameter().log(self.prime()))\ + - self.diameter().log(self.prime())\ + - other.diameter().log(other.prime()) big_metric = path_distance_metric @@ -638,39 +715,38 @@ def Hsia_kernel(self, other, basepoint): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. - OUTPUT: A finite or infinite real number + OUTPUT: A finite or infinite real number. EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2,9) - sage: Q2 = B(1/27,1/27) - sage: Q3 = B(1,1/3) - sage: Q1.Hsia_kernel(Q2,Q3) + sage: Q1 = B(2, 9) + sage: Q2 = B(1/27, 1/27) + sage: Q3 = B(1, 1/3) + sage: Q1.Hsia_kernel(Q2, Q3) 0.111111111111111 :: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2,9) + sage: Q1 = B(2, 9) sage: Q2 = B(1/2) sage: Q3 = B(1/2) - sage: Q1.Hsia_kernel(Q2,Q3) + sage: Q1.Hsia_kernel(Q2, Q3) +infinity """ - if not isinstance(other,type(self)): - raise ValueError("Hsia kernel of a point of the Berkovich projective line " + \ - "takes another point of the Berkovich projective line, not %s" %(other)) + if not isinstance(other, type(self)): + raise TypeError('other was not a point of Berkovich space') if self.parent() != other.parent(): - raise ValueError("Hsia kernel takes two points in the same Berkovich projective line") + raise ValueError("other was an element of a different Berkovich line") if not isinstance(basepoint, type(self)): - raise ValueError("basepoint must be a point of the Berkovich projective line over Cp") + raise TypeError('basepoint was not a point of Berkovich space') if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich projective line") + raise ValueError("basepoint was an element of a different Berkovich line") if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return RR(Infinity) @@ -686,15 +762,15 @@ def small_metric(self, other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. - OUTPUT: A real number + OUTPUT: A real number. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(1/4,4) - sage: Q2 = B(1/4,6) + sage: Q1 = B(1/4, 4) + sage: Q2 = B(1/4, 6) sage: Q1.small_metric(Q2) 0.0833333333333333 @@ -705,18 +781,27 @@ def small_metric(self, other): sage: Q2 = B(99) sage: Q1.small_metric(Q2) 1.00000000000000 + + :: + + sage: Q3 = B(1/4, 4) + sage: Q3.small_metric(Q2) + 1.75000000000000 + + :: + + sage: Q2.small_metric(Q3) + 1.75000000000000 """ - if not isinstance(other,Berkovich_Element_Cp): - raise ValueError("hyperbolic metric not defined between point " + \ - "of Berkovich space and %s" %(other)) + if not isinstance(other, Berkovich_Element_Cp): + raise TypeError('other was not a point of Berkovich space') if self.parent() != other.parent(): - raise ValueError("input to hyperbolic metric was an element " + \ - "of a different Berkovich space") - gauss = self.parent()(RR(0),RR(1)) + raise ValueError('other was a point of a different Berkovich space') + gauss = self.parent()(RR(0), RR(1)) g_greater_than_s = gauss.partial_order(self) g_greater_than_o = gauss.partial_order(other) if g_greater_than_s and g_greater_than_o: - return 2*((self.join(other,gauss)).diameter()) - self.diameter() - other.diameter() + return 2*(self.join(other,gauss).diameter()) - self.diameter() - other.diameter() if not g_greater_than_s: new_self = self.involution_map() else: @@ -725,9 +810,8 @@ def small_metric(self, other): new_other = other.involution_map() else: new_other = other - return 2*((new_self.join(new_other,gauss)).diameter()) \ - -new_self.diameter() \ - -new_other.diameter() + return 2*(new_self.join(new_other,gauss).diameter()) \ + - new_self.diameter() - new_other.diameter() def Hsia_kernel_infinity(self, other): r""" @@ -738,15 +822,15 @@ def Hsia_kernel_infinity(self, other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. - OUTPUT: A real number + OUTPUT: A real number. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(1/4,4) - sage: Q2 = B(1/4,6) + sage: Q1 = B(1/4, 4) + sage: Q2 = B(1/4, 6) sage: Q1.Hsia_kernel_infinity(Q2) 6.00000000000000 @@ -757,7 +841,7 @@ def Hsia_kernel_infinity(self, other): sage: ideal = A.ideal(-1/2*a^2 + a - 3) sage: B = Berkovich_Cp_Projective(A, ideal) sage: Q1 = B(4) - sage: Q2 = B(0,1.5) + sage: Q2 = B(0, 1.5) sage: Q1.Hsia_kernel_infinity(Q2) 1.50000000000000 """ @@ -768,39 +852,38 @@ def center(self): Returns the center of the corresponding disk (or sequence of disks) in `\CC_p`. - OUTPUT: An element of the ``base_ring`` of the parent Berkovich - space. + OUTPUT: An element of the ``base`` of the parent Berkovich space. EXAMPLES:: sage: B = Berkovich_Cp_Affine(3) - sage: B(3,1).center() + sage: B(3, 1).center() 3 + O(3^21) :: sage: C = Berkovich_Cp_Projective(3) - sage: C(3,1).center() + sage: C(3, 1).center() (3 + O(3^21) : 1 + O(3^20)) :: sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.ideal(-1/2*a^2 + a - 3) sage: B = Berkovich_Cp_Projective(A, ideal) sage: B(a^2+4).center() (a^2 + 4 : 1) """ if self._type == 4: - return self._center_lst[:] + return self._center_lst return self._center def type_of_point(self): """ Returns the type of this point of Berkovich space over ``Cp`` - OUTPUT: An integer between 1 and 4 inclusive + OUTPUT: An integer between 1 and 4 inclusive. EXAMPLES:: @@ -810,16 +893,16 @@ def type_of_point(self): :: - sage: B(0,1).type_of_point() + sage: B(0, 1).type_of_point() 2 """ - return self._type + return ZZ(self._type) def prime(self): """ - Shorthand for residue characteristic of the parent + The residue characteristic of the parent. - OUTPUT: A prime integer + OUTPUT: A prime integer. EXAMPLES:: @@ -827,7 +910,7 @@ def prime(self): sage: B(1).prime() 3 """ - return self._p + return ZZ(self._p) def __ne__(self, other): """ @@ -836,8 +919,8 @@ def __ne__(self, other): EXAMPLES:: sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(3,3**(1/2)) - sage: Q2 = B(3,RR(3**(1/2))) + sage: Q1 = B(3, 3**(1/2)) + sage: Q2 = B(3, RR(3**(1/2))) sage: Q1 != Q2 False """ @@ -850,7 +933,7 @@ def _repr_(self): EXAMPLES:: sage: B = Berkovich_Cp_Projective((3)) - sage: B(2,1) + sage: B(2, 1) Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 """ if self._type == 1: @@ -858,7 +941,7 @@ def _repr_(self): elif self._type == 2: return "Type II point centered at " \ + format(self._center) \ - + " of radius %s^%s" %(self._p,self._power) + + " of radius %s^%s" %(self._p, self._power) elif self._type == 3: return "Type III point centered at " \ + format(self._center) + " of radius " \ @@ -867,11 +950,11 @@ def _repr_(self): if self._center_func != None and self._radius_func != None: return "Type IV point of precision %s " %self._prec + \ "with centers given by %s and radii given by %s"\ - %(self._center_func,self._radius_func) + %(self._center_func, self._radius_func) else: return "Type IV point of precision %s, approximated " %self._prec + \ "by disks centered at %s ... with radii %s ..." \ - %(self._center_lst[:2],self._radius_lst[:2]) + %(self._center_lst[:min(self._prec, 2)], self._radius_lst[:min(self._prec, 2)]) def _latex_(self): r""" @@ -880,7 +963,7 @@ def _latex_(self): EXAMPLES:: sage: B = Berkovich_Cp_Projective((3)) - sage: latex(B(2,1)) + sage: latex(B(2, 1)) \text{type 2 Point of } \text{Projective Berkovich line over } \Bold{C}_{3} \text{equivalent to the disk centered at (2 + O(3^20) : 1 + O(3^20)) of radius 1.00000000000000 in } \Bold{C}_3 @@ -899,7 +982,7 @@ def _latex_(self): class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" - Element class of the Berkovich affine line over `\CC_p` + Element class of the Berkovich affine line over `\CC_p`. Elements are categorized into four types, represented by specific data: @@ -913,7 +996,8 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): and a radius in `[0,\infty)`. - Type IV points are represented by a finite list of centers in the ``base`` of the parent - Berkovich space and a finite list of radii in `[0,\infty)`. + Berkovich space and a finite list of radii in `[0,\infty)`. Type IV points can be created + from univariate functions, allowing for arbitrary precision. INPUT: @@ -929,9 +1013,9 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): computes the radii (computation starts at 1). - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies - the power of ``p`` such that `p^\text{power}` = radius + the power of ``p`` such that `p^\text{power}` = radius. - - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point + - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point. - ``error_check`` -- (default: True) If error checking should be run on input. If input is correctly formatted, can be set to ``False`` for better performance. @@ -958,12 +1042,12 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: b = B(2, 3**(1/2)); b Type II point centered at 2 + O(3^20) of radius 3^1/2 - sage: c = B(2,1.6); c + sage: c = B(2, 1.6); c Type III point centered at 2 + O(3^20) of radius 1.60000000000000 Some type II points may be mistaken for type III points:: - sage: b = B(3, 3**0.5); b + sage: b = B(3, 3**0.5); b #not tested Type III point centered at 3 + O(3^21) of radius 1.73205080756888 To avoid these errors, specify the power instead of the radius:: @@ -986,23 +1070,23 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: S. = PolynomialRing(RR) sage: S = FractionField(S) sage: g = (y+1)/y - sage: d = B(f,g,prec=100); d + sage: d = B(f, g, prec=100); d Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y For increased performance, error_check can be set to ``False``. WARNING: with error check set to ``False``, any error in the input will lead to incorrect results:: - sage: d = B(f,g,prec=100,error_check=False); d + sage: d = B(f, g, prec=100,error_check=False); d Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y When creating a Berkovich space backed by a number field, points can be created similarily:: sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.prime_above(3) - sage: B = Berkovich_Cp_Projective(A,ideal) + sage: B = Berkovich_Cp_Projective(A, ideal) sage: Q1 = B(a); Q1 Type I point centered at (a : 1) @@ -1013,19 +1097,19 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): TESTS:: - sage: A= Berkovich_Cp_Affine(Qp(3)) - sage: Q1=A(3,1); Q1 + sage: A = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = A(3, 1); Q1 Type II point centered at 3 + O(3^21) of radius 3^0 - sage: Q2=A(2.5,1); Q2 + sage: Q2 = A(2.5, 1); Q2 Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20) of radius 3^0 - sage: Q5=A(3,0); Q5 + sage: Q5 = A(3, 0); Q5 Type I point centered at 3 + O(3^21) - sage: A(Zp(3)(2),2).center().parent() == A(Qp(3)(2),2).center().parent() + sage: A(Zp(3)(2), 2).center().parent() == A(Qp(3)(2), 2).center().parent() True sage: Q1 == Q2 @@ -1034,10 +1118,10 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: Q1 == Q5 False - sage: Q3 = A(Qp(3)(3),power=0,error_check=False); Q3 + sage: Q3 = A(Qp(3)(3), power=0, error_check=False); Q3 Type II point centered at 3 + O(3^21) of radius 3^0 - sage: Q4 = A(3,3**0); Q4 + sage: Q4 = A(3, 3**0); Q4 Type II point centered at 3 + O(3^21) of radius 3^0 sage: Q5 = A(3, power = 1/2); Q5 @@ -1051,9 +1135,9 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: k = Qp(5) sage: R. = k[] - sage: l. = k.extension(x^2-5) - sage: B=Berkovich_Cp_Affine(Qp(5)) - sage: B(w,power=1) + sage: l. = k.extension(x^2 - 5) + sage: B = Berkovich_Cp_Affine(Qp(5)) + sage: B(w, power=1) Type II point centered at w + O(w^41) of radius 5^1 sage: C = Berkovich_Cp_Projective(QQ, 3) @@ -1072,45 +1156,59 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._base_type = parent._base_type self._ideal = parent._ideal - #if this is a point of projective berkovich space, we do the conversion + #if this is a point of projective berkovich space, we raise an error if isinstance(center, Berkovich_Element_Cp_Projective): - if center._base_type != self._base_type: - raise ValueError('cannot convert from Berkovich space over ' + \ - 'a %s to Berkovich space over a %s' %center._base_type, self._base_type) - if self._base_type == 'number field': - if center._base_space != self._base_space: - raise ValueError('cannot implicitly convert from %s to %s' \ - %(center._base_space, self._base_space)) - if (center.prime() == self._p): - try: - center.center().dehomogenize(1) - flag = False - except: - flag = True - if flag: - raise ValueError("the point at infinity of Berkovich " + \ - "projective space does not convert to Berkovich Affine space") - self._center = center.center()[0] - self._radius = center._radius - self._power = center._power - self._type = center._type - if self._type == 4: - self._prec = center._prec - self._center_func = center._center_func - self._radius_func = center._center_func - self._radius_lst = center._radius_lst - center_lst = [] - for i in center._center_lst: - center_lst.append(i[0]) - self._center_lst = center_lst - return - else: - raise ValueError("cannot convert from a point of Berkovich space " + \ - "over Cp(%s) to a " %center._base_space.base_ring().prime() + \ - "point of Berkovich space over Cp(%s)" %(parent.base().prime())) + raise TypeError('use as_affine_point to convert to affine Berkovich space') Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ - prec=prec,child="affine",error_check=error_check) + prec=prec,space_type="affine",error_check=error_check) + + def as_projective_point(self): + r""" + Returns the corresponding point of projective Berkovich space. + + We identify affine Berkovich space with the subset `P^1_{\text{Berk}}(C_p) - (1 : 0)`. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(5) + sage: B(5).as_projective_point() + Type I point centered at (5 + O(5^21) : 1 + O(5^20)) + + :: + + sage: B(0, 1).as_projective_point() + Type II point centered at (0 : 1 + O(5^20)) of radius 5^0 + + :: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q2 = B(f, g) + sage: Q2.as_affine_point() + Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) + and radii given by 40.0000000000000*pi/x + """ + new_space = Berkovich_Cp_Projective(self.parent().base_ring(), self.parent().ideal()) + if self.type_of_point() == 1: + return new_space(self.center()) + elif self.type_of_point() == 2: + return new_space(self.center(), power=self.power()) + elif self.type_of_point() == 3: + return new_space(self.center(), self.radius()) + if self._center_func == None: + center = self.center() + else: + center = self.center_function() + if self._radius_func == None: + radius = self.radius() + else: + radius = self.radius_function() + return new_space(center, radius, prec=self.prec()) def center(self): """ @@ -1118,19 +1216,19 @@ def center(self): OUTPUT: - - For type I-III points, a point of ``Cp`` - - For type IV points, a list of points of ``Cp`` + - For type I-III points, a point of ``Cp``. + - For type IV points, a list of points of ``Cp``. EXAMPLES:: sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(2,1) + sage: Q1 = B(2, 1) sage: Q1.center() 2 + O(3^20) :: - sage: d = B([4,2],[4,2]) + sage: d = B([4, 2], [4, 2]) sage: d.center() [1 + 3 + O(3^20), 2 + O(3^20)] """ @@ -1211,19 +1309,19 @@ def partial_order(self,other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. OUTPUT: - - ``True`` -- If self > other in the standard partial order - - ``False`` -- If self < other in the standard partial order - - ``None`` -- If the two points are not comparable + - ``True`` -- If self > other in the standard partial order. + - ``False`` -- If self < other in the standard partial order. + - ``None`` -- If the two points are not comparable. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2,4) - sage: Q2 = B(2,6) + sage: Q1 = B(2, 4) + sage: Q2 = B(2, 6) sage: Q1.partial_order(Q2) False @@ -1235,7 +1333,7 @@ def partial_order(self,other): :: - sage: Q4 = B(1/81,1) + sage: Q4 = B(1/81, 1) sage: print(Q4.partial_order(Q1)) None @@ -1267,17 +1365,17 @@ def join(self, other, basepoint=Infinity): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point or the string 'infty' + Berkovich space as this point or the string 'infty'. - OUTPUT: A point of the same Berkovich space + OUTPUT: A point of the same Berkovich space. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2,1) - sage: Q2 = B(2,2) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 2) sage: Q1.join(Q2) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 @@ -1290,17 +1388,17 @@ def join(self, other, basepoint=Infinity): TESTS:: - sage: Q4 = B(1/3**8+2,1) - sage: Q2.join(Q4,basepoint = Q1) + sage: Q4 = B(1/3**8+2, 1) + sage: Q2.join(Q4, basepoint = Q1) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 - sage: Q5 = B(2,1/9) - sage: Q6 = B(1,1/27) - sage: Q4.join(Q5,basepoint=Q6) + sage: Q5 = B(2, 1/9) + sage: Q6 = B(1, 1/27) + sage: Q4.join(Q5, basepoint=Q6) Type II point centered at 1 + O(3^20) of radius 3^0 - sage: Q7 = B(1/27,1/27) - sage: Q1.join(Q7,Q2) + sage: Q7 = B(1/27, 1/27) + sage: Q1.join(Q7, Q2) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 """ @@ -1343,7 +1441,7 @@ def involution_map(self): For Affine Berkovich Space, not defined for the type I point centered at 0. - OUTPUT: A point of the same Berkovich space + OUTPUT: A point of the same Berkovich space. EXAMPLES: @@ -1356,13 +1454,13 @@ def involution_map(self): :: - sage: Q2 = B(0,1/3) + sage: Q2 = B(0, 1/3) sage: Q2.involution_map() Type II point centered at 0 of radius 3^1 :: - sage: Q3 = B(1/3,1/3) + sage: Q3 = B(1/3, 1/3) sage: Q3.involution_map() Type II point centered at 3 + O(3^21) of radius 3^-3 @@ -1408,27 +1506,27 @@ def contained_in_interval(self, start, end): INPUT: - - ``start`` -- A point of the same Berkovich space as this point - - ``end`` -- A point of the same Berkovich space as this point + - ``start`` -- A point of the same Berkovich space as this point. + - ``end`` -- A point of the same Berkovich space as this point. OUTPUT: - - ``True`` if this point is an element of [``start``, ``end``] - - ``False`` otherwise + - ``True`` if this point is an element of [``start``, ``end``]. + - ``False`` otherwise. EXAMPLES:: sage: B = Berkovich_Cp_Projective((3)) - sage: Q1 = B(2,1) - sage: Q2 = B(2,4) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 4) sage: Q3 = B(1/3) - sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) + sage: Q2.contained_in_interval(Q1, Q3.join(Q1)) False :: - sage: Q4 = B(1/81,1) - sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) + sage: Q4 = B(1/81, 1) + sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) True """ if not isinstance(start, Berkovich_Element_Cp): @@ -1453,18 +1551,18 @@ def potential_kernel(self, other, basepoint): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. - OUTPUT: A finite or infinite real number + OUTPUT: A finite or infinite real number. EXAMPLES:: sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(27,1) - sage: Q2 = B(1/3,2) - sage: Q3 = B(1/9,1/2) - sage: Q3.potential_kernel(Q1,Q2) + sage: Q1 = B(27, 1) + sage: Q2 = B(1/3, 2) + sage: Q3 = B(1/9, 1/2) + sage: Q3.potential_kernel(Q1, Q2) 0.369070246428543 """ if not isinstance(other,Berkovich_Element_Cp_Affine): @@ -1489,13 +1587,13 @@ def spherical_kernel(self,other): the spherical distance on `A^1(\CC_p)` to the Berkovich Affine line. - OUTPUT: A real number + OUTPUT: A real number. EXAMPLES:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2,9) - sage: Q2 = B(1/27,1/27) + sage: Q1 = B(2, 9) + sage: Q2 = B(1/27, 1/27) sage: Q1.spherical_kernel(Q2) 0.111111111111111 @@ -1526,14 +1624,14 @@ def diameter(self, basepoint=Infinity): INPUT: - ``basepoint`` -- (default = Infinity) A point of the - same Berkovich space as this point + same Berkovich space as this point. - OUTPUT: A finite or infinite real number + OUTPUT: A finite or infinite real number. EXAMPLES:: sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(1/81,1) + sage: Q1 = B(1/81, 1) sage: Q2 = B(1/3) sage: Q1.diameter(Q2) 0.00137174211248285 @@ -1599,7 +1697,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): Type I points can be created by specifying the corresponding point of `P^1(\CC_p)`:: - sage: S = ProjectiveSpace(Qp(5),1) + sage: S = ProjectiveSpace(Qp(5), 1) sage: P = Berkovich_Cp_Projective(S); P Projective Berkovich line over Cp(5) of precision 20 @@ -1612,10 +1710,10 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): Type II and III points can be created by specifying a center and a radius:: - sage: Q3 = P((0,5),5**(3/2)); Q3 + sage: Q3 = P((0,5), 5**(3/2)); Q3 Type II point centered at (0 : 1 + O(5^20)) of radius 5^3/2 - sage: Q4 = P(0,3**(3/2)); Q4 + sage: Q4 = P(0, 3**(3/2)); Q4 Type III point centered at (0 : 1 + O(5^20)) of radius 5.19615242270663 Type IV points can be created from lists of centers and radii:: @@ -1623,9 +1721,9 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: b = S((3,2)) #create centers sage: c = S((4,3)) sage: d = S((2,3)) - sage: L = [b,c,d] + sage: L = [b, c, d] sage: R = [1.761, 1.123, 1.112] - sage: Q5 = P(L,R); Q5 + sage: Q5 = P(L, R); Q5 Type IV point of precision 3, approximated by disks centered at [(4 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20) : @@ -1643,21 +1741,21 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: R. = RR[] sage: Y = FractionField(R) sage: g = (40*pi)/x - sage: Q6 = P(f,g); Q6 + sage: Q6 = P(f, g); Q6 Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) and radii given by 40.0000000000000*pi/x TESTS:: - sage: P((1,0),3) + sage: P((1,0), 3) Traceback (most recent call last): ... - ValueError: type II and III points cannot be centered at (1 : 0) + ValueError: type II and III points can not be centered at infinity """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): #if we are given a point of Affine Berkovich Space, we do the conversion - #otherwise we call the Berkovich_Element_Cp constructor with child="projective" + #otherwise we call the Berkovich_Element_Cp constructor with space_type="projective" Element.__init__(self, parent) self._p = parent.prime() @@ -1667,31 +1765,61 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check #conversion from Affine points is handled in this constructor if isinstance(center, Berkovich_Element_Cp_Affine): - if center._base_type != self._base_type: - raise ValueError('cannot convert from Berkovich space over ' + \ - 'a %s to Berkovich space over a %s' %(center._base_type, self._base_type)) - if (center.prime() == self._p): - self._center = (self._base_space)(center._center) - self._radius = center._radius - self._power = center._power - self._type = center._type - if self._type == 4: - self._prec = center._prec - self._center_func = center._center_func - self._radius_func = center._center_func - self._radius_lst = center._radius_lst - center_lst = [] - for i in center._center_lst: - center_lst.append((self._base_space)(i)) - self._center_lst = center_lst - return - else: - raise ValueError("cannot convert from a point of Berkovich space over " + \ - "Cp(%s) to a point of Berkovich space over Cp(%s)" \ - %(center._base_space.base_ring().prime(),parent.base().prime())) + raise TypeError('use as_projective_point to convert to projective Berkovich space') Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ - prec=prec,child="projective",error_check=error_check) + prec=prec,space_type="projective",error_check=error_check) + + def as_affine_point(self): + """ + Returns the corresponding affine point after dehomogenizing at infinity. + + OUTPUT: A point of affine Berkovich space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(5) + sage: B(5).as_affine_point() + Type I point centered at 5 + O(5^21) + + :: + + sage: B(0, 1).as_affine_point() + Type II point centered at 0 of radius 5^0 + + :: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q2 = B(f, g) + sage: Q2.as_affine_point() + Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) + and radii given by 40.0000000000000*pi/x + """ + if self.center()[1] == 0: + raise ValueError('cannot convert infinity to affine Berkovich space') + new_space = Berkovich_Cp_Affine(self.parent().base_ring(), self.parent().ideal()) + if self.type_of_point() in [1,2,3]: + center = self.center()[0] + if self.type_of_point() == 1: + return new_space(center) + elif self.type_of_point() == 2: + return new_space(center, power=self.power()) + elif self.type_of_point() == 3: + return new_space(center, self.radius()) + if self._center_func == None: + center = [i[0] for i in self.center()] + else: + center = self.center_function() + if self._radius_func == None: + radius = self.radius() + else: + radius = self.radius_function() + return new_space(center, radius, prec=self.prec()) def center(self): r""" @@ -1699,19 +1827,19 @@ def center(self): OUTPUT: - - For type I-III points, a point of `P^1(\CC_p)` - - For type IV points, a list of points of `P^1(\CC_p)` + - For type I-III points, a point of `P^1(\CC_p)`. + - For type IV points, a list of points of `P^1(\CC_p)`. EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2,1) + sage: Q1 = B(2, 1) sage: Q1.center() (2 + O(3^20) : 1 + O(3^20)) :: - sage: d = B([4,2],[4,2]) + sage: d = B([4, 2], [4, 2]) sage: d.center() [(1 + 3 + O(3^20) : 1 + O(3^20)), (2 + O(3^20) : 1 + O(3^20))] """ @@ -1724,8 +1852,8 @@ def __eq__(self, other): EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B([2,2], RR(3**(1/2))) - sage: Q2 = B([1,1], 3**(1/2)) + sage: Q1 = B([2, 2], RR(3**(1/2))) + sage: Q2 = B([1, 1], 3**(1/2)) sage: Q1 == Q2 True """ @@ -1758,8 +1886,8 @@ def __hash__(self): EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: P = ProjectiveSpace(B.base_ring(),1) - sage: Q1 = B(P.point([2,2],False), RR(3**(1/2))) + sage: P = ProjectiveSpace(B.base_ring(), 1) + sage: Q1 = B(P.point([2,2], False), RR(3**(1/2))) sage: Q2 = B([1,1], 3**(1/2)) sage: hash(Q1) == hash(Q2) True @@ -1795,19 +1923,19 @@ def partial_order(self,other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. OUTPUT: - - ``True`` - If self > other in the standard partial order - - ``False`` - If other > self in the standard partial order - - ``None`` - If the two points are not comparable + - ``True`` - If self > other in the standard partial order. + - ``False`` - If other > self in the standard partial order. + - ``None`` - If the two points are not comparable. EXAMPLES:: - sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3),1)) - sage: Q1 = B(2,4) - sage: Q2 = B(2,6) + sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) + sage: Q1 = B(2, 4) + sage: Q2 = B(2, 6) sage: Q1.partial_order(Q2) False @@ -1819,14 +1947,14 @@ def partial_order(self,other): :: - sage: Q4 = B(1/81,1) + sage: Q4 = B(1/81, 1) sage: print(Q4.partial_order(Q1)) None We check infinity works in the partial order:: sage: Q5 = B((1,0)) - sage: Q6 = B(3,3) + sage: Q6 = B(3, 3) sage: Q6.partial_order(Q5) True """ @@ -1891,17 +2019,17 @@ def join(self, other, basepoint=Infinity): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point, or Infinity + Berkovich space as this point, or infinity. - OUTPUT: A point of the same Berkovich space + OUTPUT: A point of the same Berkovich space. EXAMPLES:: - sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3),1)) - sage: Q1 = B(2,1) - sage: Q2 = B(2,2) + sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 2) sage: Q1.join(Q2) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 @@ -1918,17 +2046,17 @@ def join(self, other, basepoint=Infinity): TESTS:: - sage: Q4 = B(1/3**8+2,1) - sage: Q2.join(Q4,basepoint = Q1) + sage: Q4 = B(1/3**8+2, 1) + sage: Q2.join(Q4, basepoint = Q1) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 - sage: Q5 = B(2,1/9) - sage: Q6 = B(1,1/27) - sage: Q4.join(Q5,basepoint=Q6) + sage: Q5 = B(2, 1/9) + sage: Q6 = B(1, 1/27) + sage: Q4.join(Q5, basepoint=Q6) Type II point centered at (1 + O(3^20) : 1 + O(3^20)) of radius 3^0 - sage: Q7 = B(1/27,1/27) - sage: Q1.join(Q7,Q2) + sage: Q7 = B(1/27, 1/27) + sage: Q1.join(Q7, Q2) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 sage: Q1.join(Q2, Q7) @@ -2062,7 +2190,7 @@ def involution_map(self): The involution map is the extension of the map ``z |-> 1/z`` map on `P^1(\CC_p)` to Berkovich space. - OUTPUT: A point of the same Berkovich space + OUTPUT: A point of the same Berkovich space. EXAMPLES: @@ -2075,13 +2203,13 @@ def involution_map(self): :: - sage: Q2 = B(0,1/3) + sage: Q2 = B(0, 1/3) sage: Q2.involution_map() Type II point centered at (0 : 1 + O(3^20)) of radius 3^1 :: - sage: Q3 = B(1/3,1/3) + sage: Q3 = B(1/3, 1/3) sage: Q3.involution_map() Type II point centered at (3 + O(3^21) : 1 + O(3^20)) of radius 3^-3 """ @@ -2129,27 +2257,27 @@ def contained_in_interval(self, start, end): INPUT: - - ``start`` -- A point of the same Berkovich space as this point - - ``end`` -- A point of the same Berkovich space as this point + - ``start`` -- A point of the same Berkovich space as this point. + - ``end`` -- A point of the same Berkovich space as this point. OUTPUT: - - ``True`` if this point is an element of [``start``, ``end``] - - ``False`` otherwise + - ``True`` if this point is an element of [``start``, ``end``]. + - ``False`` otherwise. EXAMPLES:: sage: B = Berkovich_Cp_Projective((3)) - sage: Q1 = B(2,1) - sage: Q2 = B(2,4) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 4) sage: Q3 = B(1/3) - sage: Q2.contained_in_interval(Q1,Q3.join(Q1)) + sage: Q2.contained_in_interval(Q1, Q3.join(Q1)) False :: - sage: Q4 = B(1/81,1) - sage: Q2.contained_in_interval(Q1,Q4.join(Q1)) + sage: Q4 = B(1/81, 1) + sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) True """ if not isinstance(start, Berkovich_Element_Cp): @@ -2192,18 +2320,18 @@ def potential_kernel(self, other, basepoint): INPUT: - - ``other`` -- A point of the same Berkovich space as this point - - ``basepoint`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. - OUTPUT: A finite or infinite real number + OUTPUT: A finite or infinite real number. EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(27,1) - sage: Q2 = B(1/3,2) - sage: Q3 = B(1/9,1/2) - sage: Q3.potential_kernel(Q1,Q2) + sage: Q1 = B(27, 1) + sage: Q2 = B(1/3, 2) + sage: Q3 = B(1/9, 1/2) + sage: Q3.potential_kernel(Q1, Q2) 0.369070246428543 """ if not isinstance(other,Berkovich_Element_Cp_Projective): @@ -2226,9 +2354,9 @@ def spherical_kernel(self,other): INPUT: - - ``other`` -- A point of the same Berkovich space as this point + - ``other`` -- A point of the same Berkovich space as this point. - OUTPUT: A real number + OUTPUT: A real number. EXAMPLES:: @@ -2269,15 +2397,15 @@ def diameter(self, basepoint=Infinity): INPUT: - - ``basepoint`` -- (default = Infinity) A point of the same - Berkovich space as this point, or infinity + - ``basepoint`` -- (default = Infinity) A point of the same + Berkovich space as this point, or infinity. - OUTPUT: A real number or infinity + OUTPUT: A real number or infinity. EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(1/81,1) + sage: Q1 = B(1/81, 1) sage: Q2 = B(1/3) sage: Q1.diameter(Q2) 0.00137174211248285 @@ -2326,6 +2454,90 @@ def residue_characteristic(self): prime = residue_characteristic + def is_padic_base(self): + """ + Returns ``True`` if this Berkovich space is backed by a p-adic field. + + OUTPUT: + + - ``True`` if this Berkovich space was created with a p-adic field + - ``False`` otherwise + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: B.is_padic_base() + True + + :: + + sage: B = Berkovich_Cp_Affine(QQ, 3) + sage: B.is_padic_base() + False + """ + return self._base_type == 'padic field' + + def is_number_field_base(self): + """ + Returns ``True`` if this Berkovich space is backed by a p-adic field. + + OUTPUT: + + - ``True`` if this Berkovich space was created with a number field + - ``False`` otherwise + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: B.is_number_field_base() + False + + :: + + sage: B = Berkovich_Cp_Affine(QQ, 3) + sage: B.is_number_field_base() + True + """ + return self._base_type == 'number field' + + def ideal(self): + r""" + The ideal which defines an embedding of the ``base_ring`` into `\CC_p`. + + If this Berkovich space is backed by a p-adic field, then an embedding is + already specified, and this returns ``None``. + + OUTPUT: + + - An ideal of a ``base_ring`` if ``base_ring`` is a number field. + + - A prime of `\QQ` if ``base_ring`` is `\QQ`. + + - ``None`` if ``base_ring`` is a p-adic field. + + EXAMPLES:: + + sage: R. = QQ[] + sage: A. = NumberField(z^2+1) + sage: ideal = A.prime_above(5) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: B.ideal() + Fractional ideal (-a - 2) + + :: + + sage: B = Berkovich_Cp_Projective(QQ, 3) + sage: B.ideal() + 3 + + :: + + sage: B = Berkovich_Cp_Projective(Qp(3)) + sage: print(B.ideal()) + None + """ + return self._ideal + def __eq__(self,right): """ Equality operator. @@ -2417,7 +2629,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): However, this method allows for more control over behind-the-scenes conversion:: - sage: B = Berkovich_Cp_Affine(Qp(3,1)); B + sage: B = Berkovich_Cp_Affine(Qp(3, 1)); B Affine Berkovich line over Cp(3) of precision 1 sage: Q1 = B(1/2); Q1 @@ -2427,7 +2639,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): with a padic field of capped-relative precision one. For high precision, pass in a high precision padic field:: - sage: B = Berkovich_Cp_Affine(Qp(3,1000)); B + sage: B = Berkovich_Cp_Affine(Qp(3, 1000)); B Affine Berkovich line over Cp(3) of precision 1000 For exact computation, a number field can be used:: @@ -2544,7 +2756,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): However, this method allows for more control over behind-the-scenes conversion:: - sage: S = Qp(3,1) + sage: S = Qp(3, 1) sage: B = Berkovich_Cp_Projective(S); B Projective Berkovich line over Cp(3) of precision 1 @@ -2558,7 +2770,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): sage: R. = QQ[] sage: A. = NumberField(x^2+1) sage: ideal = A.prime_above(2) - sage: B = Berkovich_Cp_Projective(A,ideal); B + sage: B = Berkovich_Cp_Projective(A, ideal); B Projective Berkovich line over Cp(2), with base Number Field in a with defining polynomial x^2 + 1 @@ -2620,7 +2832,7 @@ def base_ring(self): r""" The base ring of this Berkovich Space. - OUTPUT: A field + OUTPUT: A field. EXAMPLES:: @@ -2630,7 +2842,7 @@ def base_ring(self): :: - sage: C = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3,1),1)) + sage: C = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3, 1), 1)) sage: C.base_ring() 3-adic Field with capped relative precision 1 @@ -2660,7 +2872,7 @@ def _repr_(self): sage: R. = QQ[] sage: A. = NumberField(x^2+1) sage: v = A.ideal(a+1) - sage: B = Berkovich_Cp_Projective(A,v); B + sage: B = Berkovich_Cp_Projective(A, v); B Projective Berkovich line over Cp(2), with base Number Field in a with defining polynomial x^2 + 1 """ if self._base_type == 'padic field': From 0c86a049b96f1ddb4bd7a7897b54f38c93bb1db0 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Wed, 22 Jul 2020 21:10:46 +0530 Subject: [PATCH 064/379] minor review commit --- src/sage/graphs/base/boost_graph.pyx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index c38d4c6e115..9496910bb1b 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1944,8 +1944,7 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al for v in vertex_list: if v not in g: - raise ValueError("the starting vertex " + str(v) + " is not in " + - "the graph") + raise ValueError(f"the starting vertex {v} is not in the graph") if vertex_list == None: vertex_list = list(g) @@ -2012,9 +2011,13 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al result = g_boost_dir.dijkstra_shortest_paths(vi) sig_off() if not result.distances.size(): - raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + raise RuntimeError("Dijkstra algorithm does not " + "work with negative weights, " + "use Bellman-Ford instead") except RuntimeError: - raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + raise RuntimeError("Dijkstra algorithm does not " + "work with negative weights, " + "use Bellman-Ford instead") else: raise ValueError(f"unknown algorithm {algorithm!r}") else: @@ -2029,9 +2032,13 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al result = g_boost_und.dijkstra_shortest_paths(vi) sig_off() if not result.distances.size(): - raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + raise RuntimeError("Dijkstra algorithm does not " + "work with negative weights, " + "use Bellman-Ford instead") except RuntimeError: - raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + raise RuntimeError("Dijkstra algorithm does not " + "work with negative weights, " + "use Bellman-Ford instead") else: raise ValueError(f"unknown algorithm {algorithm!r}") @@ -2049,6 +2056,6 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if len(vertex_list) == 1: v = vertex_list[0] - return (distances[v], predecessors[v]) + return distances[v], predecessors[v] - return (distances, predecessors) + return distances, predecessors From 33e436d58f9c4a2c5345168e188401701d3cdf12 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Wed, 22 Jul 2020 21:49:54 +0530 Subject: [PATCH 065/379] documentation added --- src/sage/graphs/base/boost_graph.pyx | 96 +++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 9496910bb1b..4a717234e5d 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1932,6 +1932,97 @@ cpdef radius_DHV(g, weight_function=None, check_weight=True): cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, algorithm=None): r""" + Compute the shortest paths to all vertices from each vertex in + ``vertex_list``. + + The input graph can be weighted: if the algorithm is Dijkstra, no negative + weights are allowed, while if the algorithm is Bellman-Ford, negative + weights are allowed, but there must be no negative cycle (otherwise, the + shortest paths might not exist). + + However, Dijkstra algorithm is more efficient: for this reason, we suggest + to use Bellman-Ford only if necessary (which is also the default option). + + The running-time for each vertex is `O(n \log n+m)` for Dijkstra algorithm + and `O(mn)` for Bellman-Ford algorithm, where `n` is the number of nodes and + `m` is the number of edges. + + INPUT: + + - ``g`` -- the input Sage graph + + - ``vertex_list`` -- the list of vertices to compute shortest paths from + + - ``weight_function`` -- function (default: ``None``); a function that + associates a weight to each edge. If ``None`` (default), the weights of + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. + + - ``algorithm`` -- string (default: ``None``); one of the following + algorithms: + + - ``'Dijkstra'``, ``'Dijkstra_Boost'``: the Dijkstra algorithm implemented + in Boost (works only with positive weights) + + - ``'Bellman-Ford'``, ``'Bellman-Ford_Boost'``: the Bellman-Ford algorithm + implemented in Boost (works also with negative weights, if there is no + negative cycle) + + OUTPUT: + + A pair of dictionaries of dictionaries ``(distances, predecessors)`` such + that for each vertex ``v`` in ``vertex_list``, ``distances[v]`` store the + shortest distances of all the other vertices from ``v``, ``predecessors[v]`` + store the last vertices in the shortest path from ``v`` to all the other + vertices. + + EXAMPLES: + + Undirected graphs:: + + sage: from sage.graphs.base.boost_graph import shortest_paths_from_vertices + sage: g = Graph([(0,1,1),(1,2,2),(1,3,4),(2,3,1)], weighted=True) + sage: shortest_paths_from_vertices(g,[1,2]) + ({1: {0: 1, 1: 0, 2: 2, 3: 3}, 2: {0: 3, 1: 2, 2: 0, 3: 1}}, + {1: {0: 1, 1: None, 2: 1, 3: 2}, 2: {0: 1, 1: 2, 2: None, 3: 2}}) + + Directed graphs:: + + sage: g = DiGraph([(0,1,1),(1,2,-1),(2,0,2),(2,3,1)], weighted=True) + sage: shortest_paths_from_vertices(g,1) + ({0: 1, 1: 0, 2: -1, 3: 0}, {0: 2, 1: None, 2: 1, 3: 2}) + + TESTS: + + Given an input which is not a graph:: + + sage: shortest_paths_from_vertices("X-AE A-12", 1) + Traceback (most recent call last): + ... + TypeError: the input must be a Sage graph + + If there is a negative cycle:: + + sage: g = DiGraph([(0,1,1),(1,2,-2),(2,0,0.5),(2,3,1)], weighted=True) + sage: shortest_paths_from_vertices(g, 1) + Traceback (most recent call last): + ... + ValueError: the graph contains a negative cycle + + If Dijkstra is used with negative weights:: + + sage: g = Graph([(0,1,1),(1,2,-2),(1,3,4)], weighted=True) + sage: shortest_paths_from_vertices(g, 1, algorithm='Dijkstra') + Traceback (most recent call last): + ... + RuntimeError: Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead + + Wrong starting vertex:: + + sage: shortest_paths_from_vertices(g, 55) + Traceback (most recent call last): + ... + ValueError: the starting vertex 55 is not in the graph """ import sys from sage.graphs.generic_graph import GenericGraph @@ -2004,7 +2095,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al sig_on() result = g_boost_dir.bellman_ford_shortest_paths(vi) sig_off() - + if not result.distances.size(): + raise ValueError("the graph contains a negative cycle") elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: try: sig_on() @@ -2025,6 +2117,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al sig_on() result = g_boost_und.bellman_ford_shortest_paths(vi) sig_off() + if not result.distances.size(): + raise ValueError("the graph contains a negative cycle") elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: try: From 65b95f03c6f3e4ea54167627edc252dedafce3d8 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 22 Jul 2020 16:44:53 -0400 Subject: [PATCH 066/379] 29844: more documentation fixes, more tests added --- src/sage/schemes/berkovich/berkovich_space.py | 531 +++++++++++------- 1 file changed, 323 insertions(+), 208 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 77f347386b4..9684ae52e19 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -31,6 +31,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import Element +from sage.categories.number_fields import NumberFields from sage.categories.topological_spaces import TopologicalSpaces from sage.symbolic.expression import is_Expression from sage.rings.real_mpfr import RR, is_RealNumber @@ -38,7 +39,7 @@ from sage.rings.padics.padic_base_generic import pAdicBaseGeneric from sage.rings.padics.generic_nodes import is_pAdicField from sage.rings.padics.factory import Qp -from sage.schemes.projective.projective_space import ProjectiveSpace +from sage.schemes.projective.projective_space import is_ProjectiveSpace, ProjectiveSpace from sage.schemes.projective.projective_point import SchemeMorphism_point_projective_field from sage.schemes.generic.morphism import is_SchemeMorphism from sage.schemes.affine.affine_space import AffineSpace @@ -46,7 +47,6 @@ from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal -from sage.rings.number_field.number_field import is_NumberField from sage.rings.infinity import Infinity def is_Berkovich(space): @@ -251,8 +251,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] dist = self._custom_abs(center[0] - previous_center[0]) - if previous_radius < radius or dist > radius: - raise ValueError("sequence of disks does not define a type IV point as" + \ + if previous_radius < radius or dist > previous_radius: + raise ValueError("sequence of disks does not define a type IV point as " + \ "containment is not proper") self._center_lst[i] = center self._radius_lst[i] = radius @@ -295,7 +295,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= previous_center = self._center_lst[i-1] previous_radius = self._radius_lst[i-1] dist = self._custom_abs(center - previous_center) - if previous_radius < radius or dist > radius: + if previous_radius < radius or dist > previous_radius: raise ValueError("sequence of disks does not define a type IV point as " + \ "containment is not proper") self._center_lst[i] = center @@ -475,6 +475,7 @@ def center_function(self): EXAMPLES:: + sage: B = Berkovich_Cp_Projective(5) sage: L. = PolynomialRing(Qp(5)) sage: T = FractionField(L) sage: f = T(1/t) @@ -502,6 +503,7 @@ def radius_function(self): EXAMPLES:: + sage: B = Berkovich_Cp_Projective(5) sage: L. = PolynomialRing(Qp(5)) sage: T = FractionField(L) sage: f = T(1/t) @@ -689,9 +691,9 @@ def path_distance_metric(self, other): 0 """ if not isinstance(other, type(self)): - raise TypeError('other was not a point of Berkovich space') + raise TypeError('other must be a point of Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError("other was an element of a different Berkovich space") + raise ValueError("other must be a point of the same Berkovich space") if self.type_of_point() == 1 or other.type_of_point() == 1: if self == other: return 0 @@ -740,13 +742,13 @@ def Hsia_kernel(self, other, basepoint): """ if not isinstance(other, type(self)): - raise TypeError('other was not a point of Berkovich space') + raise TypeError('other must be a point of Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError("other was an element of a different Berkovich line") + raise ValueError("other must be a point of the same Berkovich space") if not isinstance(basepoint, type(self)): - raise TypeError('basepoint was not a point of Berkovich space') + raise TypeError('basepoint must be a point of Berkovich space. basepoint was %s' %basepoint) if basepoint.parent() != self.parent(): - raise ValueError("basepoint was an element of a different Berkovich line") + raise ValueError("basepoint must be a point of the same Berkovich space") if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return RR(Infinity) @@ -794,9 +796,9 @@ def small_metric(self, other): 1.75000000000000 """ if not isinstance(other, Berkovich_Element_Cp): - raise TypeError('other was not a point of Berkovich space') + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError('other was a point of a different Berkovich space') + raise ValueError('other must be a point of the same Berkovich space') gauss = self.parent()(RR(0), RR(1)) g_greater_than_s = gauss.partial_order(self) g_greater_than_o = gauss.partial_order(other) @@ -1139,13 +1141,6 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: B = Berkovich_Cp_Affine(Qp(5)) sage: B(w, power=1) Type II point centered at w + O(w^41) of radius 5^1 - - sage: C = Berkovich_Cp_Projective(QQ, 3) - sage: C(Q1) - Traceback (most recent call last): - ... - ValueError: cannot convert from Berkovich space over - a padic field to Berkovich space over a number field """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space @@ -1160,8 +1155,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check if isinstance(center, Berkovich_Element_Cp_Projective): raise TypeError('use as_affine_point to convert to affine Berkovich space') - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ - prec=prec,space_type="affine",error_check=error_check) + Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, \ + prec=prec, space_type="affine", error_check=error_check) def as_projective_point(self): r""" @@ -1189,7 +1184,7 @@ def as_projective_point(self): sage: Y = FractionField(R) sage: g = (40*pi)/x sage: Q2 = B(f, g) - sage: Q2.as_affine_point() + sage: Q2.as_projective_point() Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) and radii given by 40.0000000000000*pi/x """ @@ -1212,7 +1207,7 @@ def as_projective_point(self): def center(self): """ - Returns the center of the corresponding disk (or sequence of disks) in ``Cp`` + Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. OUTPUT: @@ -1245,6 +1240,24 @@ def __eq__(self, other): sage: Q2 = B(1, 3**(1/2)) sage: Q1 == Q2 True + + :: + + sage: Q3 = B(1) + sage: Q4 = B(4) + sage: Q3 == Q4 + False + + :: + + sage: Q5 = B(1, 4) + sage: Q1 == Q5 + False + + :: + + sage: Q1 == Q3 + False """ if other is self: return True @@ -1292,12 +1305,12 @@ def __hash__(self): if self.type_of_point() == 1: return hash(str(self.center())) elif self.type_of_point() == 4: - raise ValueError('hash not defined for type IV points') + raise NotImplementedError('hash not defined for type IV points') return hash(str(self.radius())) def partial_order(self,other): r""" - The standard partial order on Berkovich space + The standard partial order on Berkovich space. Roughly, the partial order corresponds to containment of the corresponding disks in `\CC_p`. @@ -1334,26 +1347,20 @@ def partial_order(self,other): :: sage: Q4 = B(1/81, 1) - sage: print(Q4.partial_order(Q1)) - None + sage: Q4.partial_order(Q1) is None + True :: - sage: print(Q4.partial_order(Q3)) - None + sage: Q4.partial_order(Q3) is None + True """ #error check, then convert to projective berkovich space to do the partial order - if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("partial order takes another point of " + \ - "the Berkovich Affine line, not %s" %(other)) + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError("partial order takes another point of " + \ - "the same Berkovich Affine line") - base = self._base_space - B = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) - proj_self = B(self) - proj_other = B(other) - return proj_self.partial_order(proj_other) + raise ValueError('other must be a point of the same affine Berkovich space') + return self.as_projective_point().partial_order(other.as_projective_point()) def join(self, other, basepoint=Infinity): """ @@ -1367,7 +1374,7 @@ def join(self, other, basepoint=Infinity): - ``other`` -- A point of the same Berkovich space as this point. - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point or the string 'infty'. + Berkovich space as this point or Infinity. OUTPUT: A point of the same Berkovich space. @@ -1379,63 +1386,65 @@ def join(self, other, basepoint=Infinity): sage: Q1.join(Q2) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + :: + sage: Q3 = B(5) sage: Q3.join(Q1) Type II point centered at 2 + 3 + O(3^20) of radius 3^0 + :: + sage: Q3.join(Q1, basepoint=Q2) Type II point centered at 2 + O(3^20) of radius 3^0 TESTS:: - sage: Q4 = B(1/3**8+2, 1) + sage: Q4 = B(1/3**8 + 2, 1) sage: Q2.join(Q4, basepoint = Q1) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + :: + sage: Q5 = B(2, 1/9) sage: Q6 = B(1, 1/27) sage: Q4.join(Q5, basepoint=Q6) Type II point centered at 1 + O(3^20) of radius 3^0 + :: + sage: Q7 = B(1/27, 1/27) sage: Q1.join(Q7, Q2) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 - """ #we error check and then pass to projective space to do the join - if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("join of a point of the Berkovich Affine line " + \ - "takes another point of the Berkovich Affine line, not %s" %(other)) + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError("join takes two points in the same Berkovich Affine line") + raise ValueError('other must be a point of the same affine Berkovich space') if self.type_of_point() == 4 or other.type_of_point() == 4: raise NotImplementedError("join with type IV points not implemented") - parent = self.parent() - base = self._base_space - B = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) - proj_self = B(self) - proj_other = B(other) + proj_self = self.as_projective_point() + proj_other = other.as_projective_point() if basepoint == Infinity: - return parent(proj_self.join(proj_other)) + return proj_self.join(proj_other).as_affine_point() if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise ValueError("basepoint for join must be a point of the Berkovich " + \ - "Affine line over Cp") + raise TypeError('basepoint must a point of affine Berkovich space. basepoint was %s' %basepoint) if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") + raise ValueError("basepoint must be a point of the same affine Berkovich space") if basepoint.type_of_point() == 4: raise NotImplementedError("join not implemented for type IV basepoint") - proj_basepoint = B(basepoint) - return parent(proj_self.join(proj_other, proj_basepoint)) + proj_basepoint = basepoint.as_projective_point() + return proj_self.join(proj_other, proj_basepoint).as_affine_point() def involution_map(self): r""" Returns the image of this point under the involution map. - The involution map is the extension of the map ``z |-> 1/z`` map + The involution map is the extension of the map ``z |-> 1/z`` on `\CC_p` to Berkovich space. For Affine Berkovich Space, not defined for the type I @@ -1465,15 +1474,12 @@ def involution_map(self): Type II point centered at 3 + O(3^21) of radius 3^-3 """ - parent = self.parent() - center = self.center() - if self.type_of_point() == 1: - if center == 0: + if self.center() == 0: raise ValueError("involution map not deffined on affine type I point centered at 0") - return parent(1/center) + return self.parent()(1/self.center()) - zero = parent(QQ(0)) + zero = self.parent()(QQ(0)) radius = self.radius() if self.type_of_point() in [2,3]: @@ -1481,24 +1487,24 @@ def involution_map(self): if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() - return parent(0,power=-power) - return parent(0,RR(1/radius)) - return parent(1/center,RR(radius/(self._custom_abs(center)**2))) + return self.parent()(0, power=-power) + return self.parent()(0, RR(1/radius)) + return self.parent()(1/self.center(), RR( radius / (self._custom_abs(self.center())**2) ) ) new_center_lst = [] new_radius_lst = [] for i in range(len(center)): - berk_point = parent(center[i],radius[i]) + berk_point = self.parent()(self.center()[i], radius[i]) zero_check = berk_point.partial_order(zero) if zero_check: new_center = 0 new_radius = RR(1/radius[i]) else: - new_center = 1/center[i] - new_radius = RR(radius[i]/(self._custom_abs(center[i])**2)) + new_center = 1/self.center()[i] + new_radius = RR(radius[i] / (self._custom_abs(self.center()[i])**2)) new_center_lst.append(new_center) new_radius_lst.append(new_radius) - return parent(new_center_lst,new_radius_lst,error_check=False) + return self.parent()(new_center_lst, new_radius_lst, error_check=False) def contained_in_interval(self, start, end): """ @@ -1529,22 +1535,23 @@ def contained_in_interval(self, start, end): sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) True """ - if not isinstance(start, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") + if not isinstance(start, Berkovich_Element_Cp_Affine): + raise TypeError("start must be a point of affine Berkovich space. start was %s" %start) if start.parent() != self.parent(): raise ValueError("start must be a point of the same Berkovich space as this point") - if not isinstance(end, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") + if not isinstance(end, Berkovich_Element_Cp_Affine): + raise TypeError("end must be a point of affine Berkovich space. end was %s" %end) if end.parent() != self.parent(): - raise ValueError("start must be a point of the same Berkovich space as this point") + raise ValueError("end must be a point of the same Berkovich space as this point") - base = self._base_space - P = Berkovich_Cp_Projective(ProjectiveSpace(base,1)) - return P(self).contained_in_interval(P(start),P(end)) + proj_self = self.as_projective_point() + proj_start = start.as_projective_point() + proj_end = end.as_projective_point() + return proj_self.contained_in_interval(proj_start, proj_end) def potential_kernel(self, other, basepoint): """ - The potential kernel of this point with ``other``, with basepoint ``basepoint``. + The potential kernel of this point with ``other`` with basepoint ``basepoint``. The potential kernel is the hyperbolic distance between ``basepoint`` and the join of this point with ``other`` relative to ``basepoint``. @@ -1565,19 +1572,15 @@ def potential_kernel(self, other, basepoint): sage: Q3.potential_kernel(Q1, Q2) 0.369070246428543 """ - if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("potential kernel of a point of the " + \ - "Berkovich Affine line takes another point of the Berkovich " + \ - "Affine line, not %s" %(other)) + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError("potential kernel takes two points in the " + \ - "same Berkovich Affine line") + raise ValueError('other must be a point of the same affine Berkovich space') if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise ValueError("basepoint must be a point of the Berkovich " + \ - "Affine line over Cp") + raise TypeError('basepoint must be a point of affine Berkovich space. basepoint was %s' %basepoint) if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich Affine line") - return basepoint.path_distance_metric((self.join(other,basepoint))) + raise ValueError('basepoint must be a point of the same affine Berkovich space') + return basepoint.path_distance_metric(self.join(other, basepoint)) def spherical_kernel(self,other): r""" @@ -1599,11 +1602,10 @@ def spherical_kernel(self,other): """ if not isinstance(other,Berkovich_Element_Cp_Affine): - raise ValueError("spherical kernel of a point of the Berkovich Affine " + \ - "line takes another point of the Berkovich Affine line, not %s" %(other)) + raise TypeError('other must be a point of an affine Berkovich space. other was %s' %other) if self.parent() != other.parent(): - raise ValueError("spherical kernel takes two points in the same Berkovich Affine line") - gauss_point = (self.parent())(RR(0),RR(1)) + raise ValueError('other was not a point of the same affine Berkovich space') + gauss_point = self.parent()(RR(0), RR(1)) w = self.join(other,gauss_point) dist = gauss_point.path_distance_metric(w) if dist == Infinity: @@ -1643,7 +1645,7 @@ def diameter(self, basepoint=Infinity): """ if basepoint == Infinity: return super().diameter() - return self.Hsia_kernel(self,basepoint) + return self.Hsia_kernel(self, basepoint) class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" @@ -1659,7 +1661,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): and a rational power of `p`. - Type III points are represented by a center in the ``base`` of the parent Berkovich space, - and a radius in `[0,\infty)`. + and by a radius, a real number, in `[0,\infty)`. - Type IV points are represented by a finite list of centers in the ``base`` of the parent Berkovich space and a finite list of radii in `[0,\infty)`. @@ -1673,8 +1675,8 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): - ``center`` -- For type I, II, and III points, the center of the corresponding disk in `P^1(\CC_p)`. If the parent Berkovich space was created using a number field - `K`, then ``center`` can be an element of `P^1(K)` or `K`. Otherwise, ``center`` can be an element - of a p-adic field, or projective space of dimension 1 over a padic field. + `K`, then ``center`` can be an element of `P^1(K)`. Otherwise, ``center`` + must be an element of a projective space of dimension 1 over a padic field. For type IV points, can be a list of centers used to approximate the point or a univariate function that computes the centers (computation starts at 1). @@ -1701,7 +1703,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: P = Berkovich_Cp_Projective(S); P Projective Berkovich line over Cp(5) of precision 20 - sage: a = S((0,1)) + sage: a = S(0,1) sage: Q1 = P(a); Q1 Type I point centered at (0 : 1 + O(5^20)) @@ -1856,6 +1858,24 @@ def __eq__(self, other): sage: Q2 = B([1, 1], 3**(1/2)) sage: Q1 == Q2 True + + :: + + sage: Q3 = B(1) + sage: Q4 = B(4) + sage: Q3 == Q4 + False + + :: + + sage: Q5 = B(1, 4) + sage: Q1 == Q5 + False + + :: + + sage: Q1 == Q3 + False """ if other is self: return True @@ -1904,7 +1924,7 @@ def __hash__(self): True """ if self.type_of_point() == 1: - return hash(str(self.center().normalize_coordinates())) + return hash(str(self.center())) elif self.type_of_point() == 4: raise ValueError('hash not defined for type IV points') return hash(str(self.radius())) @@ -1914,11 +1934,11 @@ def partial_order(self,other): The standard partial order on Berkovich space. Roughly, the partial order corresponds to containment of - the corresponding disks in ``Cp``. + the corresponding disks in ``Cp``. - For example, let x and y be points of type II or III. - If x has center `c_1` and radius `r_1` and y has center - `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + For example, let x and y be points of type II or III. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` is a subset of `D(c_2,r_2)` in `\CC_p`. INPUT: @@ -1927,7 +1947,7 @@ def partial_order(self,other): OUTPUT: - - ``True`` - If self > other in the standard partial order. + - ``True`` - If self => other in the standard partial order. - ``False`` - If other > self in the standard partial order. - ``None`` - If the two points are not comparable. @@ -1948,8 +1968,8 @@ def partial_order(self,other): :: sage: Q4 = B(1/81, 1) - sage: print(Q4.partial_order(Q1)) - None + sage: Q4.partial_order(Q1) == None + True We check infinity works in the partial order:: @@ -1957,18 +1977,80 @@ def partial_order(self,other): sage: Q6 = B(3, 3) sage: Q6.partial_order(Q5) True + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B((1,0)) + sage: Q2 = B(0) + sage: Q1.partial_order(Q2) == None + True + + :: + + sage: Q2.partial_order(Q2) + True + + :: + + sage: Q3 = B(2) + sage: Q2.partial_order(Q3) == None + True + + :: + + sage: Q4 = B(1/27, 1) + sage: Q4.partial_order(Q2) == None + True + + :: + + sage: Q2.partial_order(Q4) == None + True + + :: + + sage: Q5 = B(0,1) + sage: Q2.partial_order(Q5) + False + + :: + + sage: Q6 = B([3,2],[4,1/27]) + sage: Q4.partial_order(Q6) == None + True + + :: + + sage: Q5.partial_order(Q6) + True + + :: + + sage: Q7 = B(0,1/27) + sage: Q5.partial_order(Q7) + True + + :: + + sage: Q8 = B(0, 2) + sage: Q5.partial_order(Q8) + False + + :: + + sage: Q9 = B(1/9,10) + sage: Q5.partial_order(Q9) + False """ - if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("partial order takes another point of " + \ - "the Berkovich projective line, not %s" %(other)) + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) if self.parent() != other.parent(): - raise ValueError("partial order takes another point of the " + \ - "same Berkovich projective line") + raise ValueError('other must be a point of the same projective Berkovich space') #if self or other is infinity, we apply the involution map - parent = self.parent() - infty = parent((1,0)) - zero = parent(0) + infty = self.parent()((1,0)) + zero = self.parent()(0) if self == infty or other == infty: if self == zero or other == zero: return None @@ -1992,26 +2074,26 @@ def partial_order(self,other): if dist <= self.radius(): return True return None - if other.type_of_point() == 4: - center = other.center()[len(other.center())] + elif other.type_of_point() == 4: + center = other.center()[-1] dist = self._custom_abs(self.center()[0] - center[0]) - if dist <= self.radius(): + if dist <= self.radius() and other.radius()[-1] <= self.radius(): return True return None else: dist = self._custom_abs(self.center()[0]-other.center()[0]) - if(dist <= self.radius()): - if(other.radius() <= self.radius()): + if dist <= self.radius(): + if other.radius() <= self.radius(): return True return False else: - if(dist <= other.radius()): + if dist <= other.radius(): return False return None def join(self, other, basepoint=Infinity): """ - Computes the join of this point and ``other``, with respect to ``basepoint``. + Computes the join of this point and ``other``, with respect to ``basepoint``. The join is first point that lies on the interesection of the path from this point to ``basepoint`` and the path from ``other`` to @@ -2047,7 +2129,7 @@ def join(self, other, basepoint=Infinity): TESTS:: sage: Q4 = B(1/3**8+2, 1) - sage: Q2.join(Q4, basepoint = Q1) + sage: Q2.join(Q4, basepoint=Q1) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 sage: Q5 = B(2, 1/9) @@ -2081,28 +2163,23 @@ def join(self, other, basepoint=Infinity): sage: Q11.join(Q10) Type II point centered at (0 : 1) of radius 3^0 """ - parent = self.parent() - - if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("join of a point of the Berkovich projective " + \ - "line takes another point of the Berkovich projective line, not %s" %(other)) - if other.parent() != parent: - raise ValueError("join takes two points in the same Berkovich projective line") + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same projective Berkovich line') #if either self or other is type IV, we use the last disk in the approximation - if self.type_of_point() == 4: - new_center = self.center()[self.prec()-1] - new_radius = self.radius()[self.prec()-1] - return parent(new_center,new_radius).join(other) + new_center = self.center()[-1] + new_radius = self.radius()[-1] + return self.parent()(new_center,new_radius).join(other) if other.type_of_point() == 4: - new_center = other.center()[other.prec()-1] - new_radius = other.radius()[other.prec()-1] - return self.join(parent(new_center,new_radius)) + new_center = other.center()[-1] + new_radius = other.radius()[-1] + return self.join(self.parent()(new_center,new_radius)) #we deal with the point at infinity as a special case - - infty = parent((1,0)) + infty = self.parent()((1,0)) if basepoint == Infinity or basepoint == infty: if self == infty or other == infty: @@ -2111,22 +2188,21 @@ def join(self, other, basepoint=Infinity): maximum = max(dist, self.radius(), other.radius()) #optimize for when self or other are type II if maximum == self.radius() and self.type_of_point() == 2: - return parent(self.center(), power=self.power()) + return self.parent()(self.center(), power=self.power()) if maximum == other.radius() and other.type_of_point() == 2: - return parent(self.center(), power=other.power()) - return parent(self.center(), maximum) + return self.parent()(self.center(), power=other.power()) + return self.parent()(self.center(), maximum) if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint for join must be a point of the Berkovich projective line over Cp") - if basepoint.parent() != parent: + raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) + if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich projective line") #if the basepoint is type IV, we use the last disk in the approximation - if basepoint.type_of_point() == 4: - new_center = other.center()[other.prec()-1] - new_radius = other.radius()[other.prec()-1] - return self.join(other, parent(new_center,new_radius)) + new_center = other.center()[-1] + new_radius = other.radius()[-1] + return self.join(other, self.parent()(new_center,new_radius)) if self == infty: return other.join(basepoint) @@ -2135,21 +2211,19 @@ def join(self, other, basepoint=Infinity): #since none of the self, other, and basepoint are infinity, we can now treat them #as affine points - b_greater_than_s = basepoint.partial_order(self) b_greater_than_o = basepoint.partial_order(other) s_greater_than_o = self.partial_order(other) #we deal with all the cases where self and other are not comparable first - if s_greater_than_o == None: if b_greater_than_o == None: if b_greater_than_s == None: #case where none of the points are comparable dist_b_s = self._custom_abs(self.center()[0] - basepoint.center()[0]) dist_b_o = self._custom_abs(other.center()[0] - basepoint.center()[0]) - return parent(basepoint.center(),\ - min(max(dist_b_o,other.radius(),basepoint.radius()),\ + return self.parent()(basepoint.center(),\ + min(max(dist_b_o,other.radius(),basepoint.radius()), \ max(dist_b_s,self.radius(),basepoint.radius()))) #case where self and basepoint are comparable @@ -2167,7 +2241,6 @@ def join(self, other, basepoint=Infinity): return other #now the cases where self > other - elif s_greater_than_o: if b_greater_than_s == None: return self @@ -2179,7 +2252,6 @@ def join(self, other, basepoint=Infinity): return other #join is symmetric, so we flip self and other so that self > other - else: return other.join(self,basepoint) @@ -2187,7 +2259,7 @@ def involution_map(self): r""" Returns the image of this point under the involution map. - The involution map is the extension of the map ``z |-> 1/z`` map + The involution map is the extension of the map ``z |-> 1/z`` on `P^1(\CC_p)` to Berkovich space. OUTPUT: A point of the same Berkovich space. @@ -2196,7 +2268,7 @@ def involution_map(self): The involution map is 1/z on type I points:: - sage: B = Berkovich_Cp_Projective((3)) + sage: B = Berkovich_Cp_Projective(3) sage: Q1 = B(1/2) sage: Q1.involution_map() Type I point centered at (2 + O(3^20) : 1 + O(3^20)) @@ -2212,44 +2284,56 @@ def involution_map(self): sage: Q3 = B(1/3, 1/3) sage: Q3.involution_map() Type II point centered at (3 + O(3^21) : 1 + O(3^20)) of radius 3^-3 + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B((1,0)).involution_map() + Type I point centered at (0 : 1 + O(3^20)) + + :: + + sage: B(0).involution_map() + Type I point centered at (1 + O(3^20) : 0) + + :: + + sage: B(1/81, 1.5).involution_map() + Type III point centered at (3^4 + O(3^24) : 1 + O(3^20)) of radius 0.000228623685413809 """ - parent = self.parent() - center = self.center() - infty = parent((1,0)) - zero = parent(0) + infty = self.parent()((1,0)) + zero = self.parent()(0) if self.type_of_point() == 1: if self == infty: return zero if self == zero: return infty - return parent(1/center[0]) - - radius = self.radius() + return self.parent()(1/self.center()[0]) if self.type_of_point() in [2,3]: zero_contained_in_self = self.partial_order(zero) if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() - return parent(0,power=-power) - return parent(0,1/radius) - return parent(1/center[0],radius/(self._custom_abs(center[0])**2)) + return self.parent()(0, power=-power) + return self.parent()(0, 1/self.radius()) + return self.parent()(1/self.center()[0], self.radius()/(self._custom_abs(self.center()[0])**2)) new_center_lst = [] new_radius_lst = [] - for i in range(len(center)): - berk_point = parent(center[i],radius[i]) + for i in range(len(self.center())): + berk_point = self.parent()(self.center()[i], self.radius()[i]) zero_check = berk_point.partial_order(zero) if zero_check: new_center = 0 - new_radius = 1/radius[i] + new_radius = 1/self.radius()[i] else: - new_center = 1/center[i][0] - new_radius = radius[i]/(self._custom_abs(center[i][0])**2) + new_center = 1/self.center()[i][0] + new_radius = self.radius()[i]/(self._custom_abs(self.center()[i][0])**2) new_center_lst.append(new_center) new_radius_lst.append(new_radius) - return parent(new_center_lst,new_radius_lst) + return self.parent()(new_center_lst, new_radius_lst) def contained_in_interval(self, start, end): """ @@ -2267,7 +2351,7 @@ def contained_in_interval(self, start, end): EXAMPLES:: - sage: B = Berkovich_Cp_Projective((3)) + sage: B = Berkovich_Cp_Projective(3) sage: Q1 = B(2, 1) sage: Q2 = B(2, 4) sage: Q3 = B(1/3) @@ -2279,6 +2363,37 @@ def contained_in_interval(self, start, end): sage: Q4 = B(1/81, 1) sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) True + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: infty = B((1,0)) + sage: zero = B(0) + sage: gauss = B(0,1) + sage: infty.contained_in_interval(zero, gauss) + False + + :: + + sage: Q1 = B(1,3) + sage: infty.contained_in_interval(gauss, Q1) + False + + :: + + sage: zero.contained_in_interval(infty, gauss) + False + + :: + + sage: gauss.contained_in_interval(zero, infty) + True + + :: + + sage: Q2 = B(81, 1/3) + sage: gauss.contained_in_interval(infty, Q2) + True """ if not isinstance(start, Berkovich_Element_Cp): raise ValueError("start must be a point of Berkovich space") @@ -2290,8 +2405,8 @@ def contained_in_interval(self, start, end): raise ValueError("start must be a point of the same Berkovich space as this point") #we treat infinity as a special case - infty = (self.parent())((1,0)) - zero = (self.parent())(0) + infty = self.parent()((1, 0)) + zero = self.parent()(ZZ(0)) if self == infty: if start == zero or end == zero: return end == infty or start == infty @@ -2301,9 +2416,9 @@ def contained_in_interval(self, start, end): if self == zero: return end == zero or start == zero if start == zero or end == zero: - gauss = (self.parent())(0,1) + gauss = self.parent()(ZZ(0),ZZ(1)) return self.contained_in_interval(start,gauss) or self.contained_in_interval(gauss,end) - return (self.involution_map()).contained_in_interval(start.involution_map(),\ + return self.involution_map().contained_in_interval(start.involution_map(), \ end.involution_map()) join = start.join(end) return join.partial_order(self) and (self.partial_order(start) \ @@ -2334,16 +2449,15 @@ def potential_kernel(self, other, basepoint): sage: Q3.potential_kernel(Q1, Q2) 0.369070246428543 """ - if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("potential kernel of a point of the Berkovich " + \ - "projective line takes another point of the Berkovich projective line, not %s" %(other)) - if self.parent() != other.parent(): - raise ValueError("potential kernel takes two points in the same Berkovich projective line") + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same projective Berkovich line') if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise ValueError("basepoint must be a point of the Berkovich projective line over Cp") + raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich projective line") - return basepoint.path_distance_metric((self.join(other,basepoint))) + raise ValueError('basepoint must be a point of the same projective Berkovich line') + return basepoint.path_distance_metric(self.join(other, basepoint)) def spherical_kernel(self,other): r""" @@ -2361,8 +2475,8 @@ def spherical_kernel(self,other): EXAMPLES:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2,2) - sage: Q2 = B(1/9,1) + sage: Q1 = B(2, 2) + sage: Q2 = B(1/9, 1) sage: Q1.spherical_kernel(Q2) 0.500000000000000 @@ -2372,17 +2486,16 @@ def spherical_kernel(self,other): sage: Q3.spherical_kernel(Q3) 0 """ - if not isinstance(other,Berkovich_Element_Cp_Projective): - raise ValueError("spherical kernel of a point of the Berkovich projective " + \ - "line takes another point of the Berkovich projective line, not %s" %(other)) - if self.parent() != other.parent(): - raise ValueError("spherical kernel takes two points in the same Berkovich projective line") - gauss_point = (self.parent())(RR(0),RR(1)) + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same projective Berkovich line') + gauss_point = self.parent()(ZZ(0), ZZ(1)) w = self.join(other,gauss_point) dist = gauss_point.path_distance_metric(w) if dist == Infinity: return 0 - return (self.prime())**(-1*dist) + return self.prime()**(-1*dist) def diameter(self, basepoint=Infinity): r""" @@ -2418,7 +2531,7 @@ def diameter(self, basepoint=Infinity): if basepoint == Infinity: return super().diameter() else: - return self.Hsia_kernel(self,basepoint) + return self.Hsia_kernel(self, basepoint) class Berkovich(UniqueRepresentation, Parent): """ @@ -2597,7 +2710,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): The Berkovich Affine line over `\CC_p`. The Berkovich Affine line is the set of seminorms on `\CC_p[x]`, - with the weakest topology such that the map `| \cdot | \to |f|` continuous + with the weakest topology such that the map `| \cdot | \to |f|` is continuous for all `f \in \CC_p[x]`. INPUT: @@ -2660,7 +2773,7 @@ def __init__(self, base, ideal=None): base = Qp(base) #TODO chance to Qpbar else: raise ValueError("non-prime pased into Berkovich space") - if is_NumberField(base): + if base in NumberFields(): if ideal == None: raise ValueError('passed a number field but not an ideal') if base is not QQ: @@ -2697,6 +2810,15 @@ def _repr_(self): sage: B = Berkovich_Cp_Affine(3) sage: B Affine Berkovich line over Cp(3) of precision 20 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(z^2 + 1) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Affine(A, ideal) + Affine Berkovich line over Cp(3), with base Number Field + in a with defining polynomial z^2 + 1 """ if self._base_type == 'padic field': return "Affine Berkovich line over Cp(%s) of precision %s" %(self.prime(),\ @@ -2705,7 +2827,6 @@ def _repr_(self): return "Affine Berkovich line over Cp(%s), with base %s" %(self.prime(),\ self.base()) - def _latex_(self): r""" LaTeX representation of this Berkovich Space. @@ -2783,14 +2904,11 @@ class Berkovich_Cp_Projective(Berkovich_Cp): def __init__(self, base, ideal=None): if base in ZZ: if base.is_prime(): - base = ProjectiveSpace(Qp(base),1) + base = ProjectiveSpace(Qp(base), 1) else: raise ValueError("non-prime pased into Berkovich space") - if is_NumberField(base): + if base in NumberFields() or is_pAdicField(base): base = ProjectiveSpace(base, 1) - if is_pAdicField(base): - base = ProjectiveSpace(base, 1) - from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(base): raise ValueError("base of projective Berkovich space must be projective space") if not (is_pAdicField(base.base_ring())): @@ -2818,10 +2936,7 @@ def __init__(self, base, ideal=None): prime = base.base_ring().prime() ideal = None self._base_type = 'padic field' - if base.ambient_space() != base: - raise ValueError("base of projective Berkovich space must be " + \ - "projective space over Qp or a number field") - if base.dimension() != 1: + if base.dimension_relative() != 1: raise ValueError("base of projective Berkovich space must be " + \ "projective space of dimension 1 over Qp or a number field") self._p = prime From 363b5cc959ab30b667dc60f9cf7e075a2c8f37b6 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 22 Jul 2020 16:48:07 -0400 Subject: [PATCH 067/379] 29844: fixed failing tests --- src/sage/schemes/berkovich/berkovich_space.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 9684ae52e19..60f2615bf5b 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -2157,7 +2157,7 @@ def join(self, other, basepoint=Infinity): sage: Q10.join(Q9) Traceback (most recent call last): ... - ValueError: join takes two points in the same Berkovich projective line + ValueError: other must be a point of the same projective Berkovich line sage: Q11 = C(0, 1/3) sage: Q11.join(Q10) @@ -2816,7 +2816,7 @@ def _repr_(self): sage: R. = QQ[] sage: A. = NumberField(z^2 + 1) sage: ideal = A.prime_above(3) - sage: B = Berkovich_Cp_Affine(A, ideal) + sage: B = Berkovich_Cp_Affine(A, ideal); B Affine Berkovich line over Cp(3), with base Number Field in a with defining polynomial z^2 + 1 """ @@ -2912,7 +2912,7 @@ def __init__(self, base, ideal=None): if not is_ProjectiveSpace(base): raise ValueError("base of projective Berkovich space must be projective space") if not (is_pAdicField(base.base_ring())): - if not (is_NumberField(base.base_ring())): + if base.base_ring() not in NumberFields(): raise ValueError("base of projective Berkovich space must be " + \ "projective space over Qp or a number field") else: From f07088ea62969d74116ac3aa39f62ec478337f84 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Wed, 22 Jul 2020 17:20:31 -0400 Subject: [PATCH 068/379] 29844: split into two files --- src/sage/schemes/berkovich/all.py | 2 +- .../schemes/berkovich/berkovich_cp_element.py | 2507 ++++++++++++++++ src/sage/schemes/berkovich/berkovich_space.py | 2574 +---------------- 3 files changed, 2584 insertions(+), 2499 deletions(-) create mode 100644 src/sage/schemes/berkovich/berkovich_cp_element.py diff --git a/src/sage/schemes/berkovich/all.py b/src/sage/schemes/berkovich/all.py index 8bc437bdbf6..0c26ff503ed 100644 --- a/src/sage/schemes/berkovich/all.py +++ b/src/sage/schemes/berkovich/all.py @@ -1,5 +1,5 @@ """nodoctest -all.py -- export of projective schemes to Sage +all.py -- export of Berkovich spaces to all of Sage """ from __future__ import absolute_import diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py new file mode 100644 index 00000000000..d574c006bcc --- /dev/null +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -0,0 +1,2507 @@ +r""" +Berkovich Space over `\CC_p` + +The Berkovich affine line is the set of seminorms on `\CC_p[x]`, +with the weakest topology that makes the map `| \cdot | \to |f|` continuous +for all `f \in \CC_p[x]`. The Berkovich projective line is the +one-point compactification of the Berkovich affine line. + +The two main classes are :class:`Berkovich_Cp_Affine` and +:class:`Berkovich_Cp_Projective`, which implement the affine and +projective lines, respectively. + +:class:`Berkovich_Cp_Affine` and :class:`Berkovich_Cp_Projective` +take as input one of the following: the prime `p`, a finite +extension of `\QQ_p`, or a number field and a place. + +AUTHORS: + + - Alexander Galarraga (2020-06-22): initial implementation + +""" + +#***************************************************************************** +# 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.element import Element +from sage.symbolic.expression import is_Expression +from sage.rings.real_mpfr import RR, is_RealNumber +from sage.rings.padics.padic_generic_element import pAdicGenericElement +from sage.rings.padics.padic_base_generic import pAdicBaseGeneric +from sage.rings.padics.generic_nodes import is_pAdicField +from sage.rings.padics.factory import Qp +from sage.schemes.projective.projective_space import is_ProjectiveSpace, ProjectiveSpace +from sage.schemes.projective.projective_point import SchemeMorphism_point_projective_field +from sage.schemes.generic.morphism import is_SchemeMorphism +from sage.schemes.affine.affine_space import AffineSpace +from sage.schemes.generic.scheme import Scheme +from sage.rings.rational_field import QQ +from sage.rings.integer_ring import ZZ +from sage.rings.infinity import Infinity + + +class Berkovich_Element(Element): + """ + The parent class for any element of a Berkovich space + """ + pass + +class Berkovich_Element_Cp(Berkovich_Element): + r""" + The abstract parent class for any element of Berkovich space over `\CC_p`. + This class should never be instantiated, instead use :class:`Berkovich_Element_Cp_Affine` + or :class:`Berkovich_Element_Cp_Projective`. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(2) + Type I point centered at 2 + O(3^20) + + :: + + sage: B(0, 1) + Type II point centered at 0 of radius 3^0 + """ + + def __init__(self, parent, center, radius=None, power=None, prec=20, space_type=None, error_check=True): + """ + Initialization function. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(5) + sage: B(4) + Type I point centered at 4 + O(5^20) + """ + from sage.rings.function_field.element import is_FunctionFieldElement + from sage.rings.polynomial.polynomial_element import is_Polynomial + from sage.rings.fraction_field_element import FractionFieldElement_1poly_field + self._type = None + + #if radius is a list or a tuple, this is a type 4 point + if isinstance(radius, list) or isinstance(radius, tuple): + if error_check: + if not (isinstance(center, list) or isinstance(center, tuple)): + raise TypeError("center was passed a list but radius was not a list") + if len(radius) != len(center): + raise ValueError("the same number of centers and radii must be specified to create " + \ + "a type IV point") + self._center_lst = list(center) + self._radius_lst = list(radius) + self._prec = len(self._radius_lst) + self._center_func = None + self._radius_func = None + self._type = 4 + self._radius = None + self._center = None + if not error_check: + return + + #is_FunctionFieldElement calls .parent + elif hasattr(center, "parent") and hasattr(radius, 'parent'): + from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial + if is_MPolynomial(center): + try: + center = center.univariate_polynomial() + except: + raise TypeError('center was %s, a multivariable polynomial' %center) + + #check if the radius and the center are functions + center_func_check = is_FunctionFieldElement(center) or is_Polynomial(center) or\ + isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center) + radius_func_check = is_FunctionFieldElement(radius) or is_Polynomial(radius) or\ + isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius) + + if center_func_check: + #check that both center and radii are supported univariate function + center_expr_check = False + radius_expr_check = False + if error_check: + if is_Expression(center): + if len(center.variables()) != 1: + raise ValueError("an expression with %s " %(len(center.variables())) + \ + "variables cannot define the centers approximating a type IV point") + else: + #we do this since .subs is currently buggy for polynomials but not expressions + center_expr_check = True + if not radius_func_check: + raise TypeError("center was passed a function but radius was not a function") + if is_Expression(radius): + if len(radius.variables()) != 1: + raise ValueError("an expression with %s " %(len(radius.variables())) + \ + "variables cannot define the radii approximating a type IV point") + else: + radius_expr_check = True + else: + if is_Expression(center): + center_expr_check = True + if is_Expression(radius): + radius_expr_check = True + self._type = 4 + self._prec = prec + center_lst = [] + radius_lst = [] + self._center_func = center + self._radius_func = radius + if center_expr_check: + x = self._center_func.variables()[0] + if radius_expr_check: + y = self._radius_func.variables()[0] + for i in range(1,self._prec+1): + if center_expr_check: + #we use .subs for expressions to avoid deprecation + center_lst.append(self._center_func.subs({x:i})) + else: + #.subs for polynomials is currently buggy + center_lst.append(self._center_func(i)) + if radius_expr_check: + radius_lst.append(self._radius_func.subs({y:i})) + else: + radius_lst.append(self._radius_func(i)) + self._center_lst = center_lst + self._radius_lst = radius_lst + self._radius = None + self._center = None + if not error_check: + return + + if self._type == 4 and error_check: + if space_type == "projective": + for i in range(len(self._center_lst)): + center = self._center_lst[i] + radius = self._radius_lst[i] + #make sure the center is a point of projective space and not the point at infinity + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise TypeError('could not convert %s to %s' %(center, self._base_space)) + if self._base_type == 'padic field': + if not is_pAdicField(center.scheme().base_ring()): + if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError("could not convert %s to %s" %(center, self._base_space)) + else: + # center is padic, not but an element of a scheme over a padic field. + # we convert to scheme over a padic field + center = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1)(center) + if center.scheme().base_ring().prime() != self._p: + raise ValueError("center must be an element of " + \ + "%s not %s" %self._base_space, center.scheme()) + else: + if center not in self._base_space: + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) + if center.scheme().ambient_space() != center.scheme(): + raise ValueError("the center of a point of Berkovich space over " + \ + "P^1(Cp(%s)) must be a point of Cp not %s" %(self._p,center.scheme())) + if center == (center.scheme())((1,0)): + raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ + "space cannot be centered at %s" %((center.scheme())((1,0)))) + #since we are over a field, we can normalize coordinates. all code assumes normalized coordinates + center.normalize_coordinates() + #make sure the radius coerces into the reals + if not is_RealNumber(radius): + if is_Expression(radius): + radius = RR(radius) + elif RR.has_coerce_map_from(radius.parent()): + radius = RR(radius) + else: + raise TypeError("the radius of a disk approximating a type IV point" + \ + "must coerce into the real numbers, %s does not coerce" %(radius)) + if i != 0: + #check containment for the sequence of disks + previous_center = self._center_lst[i-1] + previous_radius = self._radius_lst[i-1] + dist = self._custom_abs(center[0] - previous_center[0]) + if previous_radius < radius or dist > previous_radius: + raise ValueError("sequence of disks does not define a type IV point as " + \ + "containment is not proper") + self._center_lst[i] = center + self._radius_lst[i] = radius + return + elif space_type == "affine": + for i in range(len(self._center_lst)): + center = self._center_lst[i] + radius = self._radius_lst[i] + if self._base_type == 'padic field': + #make sure the center is in Cp + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) + elif not is_pAdicField(center.parent()): + #center is padic, not but an element of a padic field. we convert to padic field + center = (center.parent().fraction_field())(center) + if (center.parent()).prime() != self._p: + raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) + else: + #make sure the center is in the appropriate number field + if not(center in self._base_space): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) + #make sure the radius coerces into the reals + if not is_RealNumber(radius): + if is_Expression(radius): + radius = RR(radius) + elif RR.has_coerce_map_from(radius.parent()): + radius = RR(radius) + self._radius_lst[i] = radius + else: + raise ValueError("the radius of a disk approximating a type IV point must " + \ + "coerce into the real numbers, %s does not coerce" %(radius)) + if i != 0: + #check containment for the sequence of disks + previous_center = self._center_lst[i-1] + previous_radius = self._radius_lst[i-1] + dist = self._custom_abs(center - previous_center) + if previous_radius < radius or dist > previous_radius: + raise ValueError("sequence of disks does not define a type IV point as " + \ + "containment is not proper") + self._center_lst[i] = center + self._radius_lst[i] = radius + return + else: + raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ + "Berkovich_Element_Cp directly" ) + return + + #the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type + if error_check: + if space_type == 'affine': + if self._base_type == 'padic field': + #make sure the center is in Cp + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) + elif not is_pAdicField(center.parent()): + #center is padic, not but an element of a padic field. we convert to padic field + center = (center.parent().fraction_field())(center) + if (center.parent()).prime() != self._p: + raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) + else: + #make sure the center is in the appropriate number field + if not(center in self._base_space): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) + elif space_type == "projective": + if not isinstance(center, SchemeMorphism_point_projective_field): + try: + center = (self._base_space)(center) + except (ValueError, TypeError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) + if self._base_type == 'padic field': + if not is_pAdicField(center.scheme().base_ring()): + if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError("could not convert %s to %s" %(center, self._base_space)) + else: + # center is padic, not but an element of a scheme over a padic field. + # we convert to scheme over a padic field + field_scheme = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1) + try: + center = field_scheme(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %center, field_scheme) + if center.scheme().base_ring().prime() != self._p: + raise ValueError("center must be an element of " + \ + "%s not %s" %self._base_space, center.scheme()) + else: + if center not in self._base_space: + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) + if not(center.scheme().ambient_space() is center.scheme()): + raise ValueError("the center of a point of projective Berkovich space cannot be " + \ + "a point of %s" %(center.scheme())) + #since we are over a field, we normalize coordinates + center.normalize_coordinates() + else: + raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ + "Berkovich_Element_Cp directly") + + self._center = center + + #since this point is not type IV, these are None + self._center_func = None + self._center_lst = None + self._radius_lst = None + self._radius_func = None + + if (radius == None and power == None) or radius == 0: + self._type = 1 + self._radius = 0 + self._power = None + return + #In order to simplify our representation, type II and III points cannot be centered at infinity + if space_type == "projective": + #TODO use involution map to allow for infinity to be passed in as center + if center[1] == 0: + raise ValueError('type II and III points can not be centered at infinity') + if power != None: + if error_check: + if not(power.parent() is QQ): + try: + power = QQ(power) + except TypeError: + raise TypeError("power must convert to rationals") + if radius != None: + if radius != RR(self._p**power): + raise ValueError("conflicting inputs for power and radius") + self._power = power + self._radius = RR(self._p**power) + self._type = 2 + return + if radius != None: + if is_Expression(radius): + try: + power = QQ((radius.log(self._p)).expand_log()) + except TypeError: + pass + try: + radius = RR(radius) + self._radius = radius + except TypeError: + if len(radius.variables()) == 1: + raise ValueError('radius univariate function but center is constant. ' + \ + 'this does not define a type IV point') + raise TypeError("symbolic radius must be a real number") + if (not is_RealNumber(radius)) and power == None: + if RR.has_coerce_map_from(radius.parent()): + self._radius = RR(radius) + else: + raise TypeError("radius must coerce into real numbers") + else: + self._radius = radius + if power != None: + self._power = power + self._type = 2 + return + power = RR(radius.log(self._p)) + if power.is_integer(): + self._power = QQ(power) + self._type = 2 + else: + self._type = 3 + self._power = power + return + + raise TypeError('unknown error constructing point of Berkovich space over Cp') + + def _custom_abs(self, x): + """ + Returns the absolute value of ``x`` with respect to the norm on ``Cp``. + + Used to simplify code, as ``x`` may be a point of a number field + or a padic field. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(QQ, 3) + sage: Q1 = B(9) + sage: Q1._custom_abs(Q1.center()) + 1/9 + + :: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(9) + sage: Q1._custom_abs(Q1.center()) + 1/9 + """ + if self._base_type == 'padic field': + return x.abs() + if x.valuation(self._ideal) == Infinity: + return 0 + if self._ideal in QQ: + return self.prime()**(-1*x.valuation(self._ideal)) + return self.prime()**(-1*x.valuation(self._ideal)/self._ideal.absolute_ramification_index()) + + def center_function(self): + """ + Returns the function defining the centers of disks in the approximation. + + Not defined unless this point is a type IV point created by using + a univariate function to compute centers. + + OUTPUT: A univariate function. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(5) + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q1 = B(f, g) + sage: Q1.center_function() + (1 + O(5^20))/((1 + O(5^20))*t) + """ + if self.type_of_point() != 4: + raise ValueError('center_function not defined for points which are not type IV') + if self._center_func == None: + raise ValueError('this type IV point does not have a center function') + return self._center_func + + def radius_function(self): + """ + Returns the function defining the radii of disks in the approximation. + + Not defined unless this point is a type IV point created by using + a univariate function to compute radii. + + OUTPUT: A univariate function. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(5) + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q1 = B(f, g) + sage: Q1.radius_function() + 40.0000000000000*pi/x + """ + if self.type_of_point() != 4: + raise ValueError('center_function not defined for points which are not type IV') + if self._radius_func == None: + raise ValueError('this type IV point does not have a radius function') + return self._radius_func + + def precision(self): + """ + Returns the precision of a type IV point. + + Not defined for type I, II, or III points. + + OUTPUT: An integer. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) + sage: d.precision() + 3 + + TESTS:: + + sage: d.precision == d.prec + True + """ + if self._type in [1,2,3]: + raise AttributeError("type I, II, and III points do not have a precision") + return self._prec + + prec = precision + + def power(self): + r""" + Power of ``p`` such that `p^\text{power} = \text{radius}`. + + For type II points, always in `\QQ`. For type III points, + a real number. Not defined for type I or IV points. + + OUTPUT: + + - A rational for type II points. + - A real number for type III points. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1, 9) + sage: Q1.power() + 2 + + :: + + sage: Q2 = B(1, 4) + sage: Q2.power() + 1.26185950714291 + """ + if self._type in [1,4]: + raise AttributeError("type I and IV points do not have a power") + return self._power + + def radius(self): + """ + Radius of the corresponding disk (or sequence of disks) in ``Cp``. + + OUTPUT: + + - A non-negative real number for points Types I-III. + - A list of non-negative real numbers for type IV points. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1, 2/5) + sage: Q1.radius() + 0.400000000000000 + + :: + + sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) + sage: d.radius() + [1.76100000000000, 1.12300000000000, 1.11200000000000] + """ + if self._type == 4: + return self._radius_lst + return self._radius + + def diameter(self): + """ + Diameter function on Berkovich space. + + For type I, II, and III points, returns the radius. + + For type IV points returns either the last radius + in the finite approximation, or if a generating function + was given for the radii, the diameter is computed + as the limit of the function as it's variable tends + to infinity. + + OUTPUT: A real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(3) + sage: Q1.diameter() + 0 + + :: + + sage: Q2 = B(1/2, 9) + sage: Q2.diameter() + 9.00000000000000 + + The diameter of a type IV point is the limit of the radii:: + + sage: R. = PolynomialRing(Qp(3)) + sage: f = R(2) + sage: S. = PolynomialRing(RR) + sage: S = FractionField(S) + sage: g = (y+1)/y + sage: B(f,g).diameter() + 1.0 + """ + if self._type == 4: + if self._radius_func == None: + return self._radius_lst[-1] + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(QQ, names="x") + x = R.gens()[0] + if is_Expression(self._radius_func): + radius_func_variable = self._radius_func.variables()[0] + radius_expr = self._radius_func.subs({radius_func_variable:x}) + else: + radius_expr = self._radius_func(x) + from sage.symbolic.ring import SymbolicRing as SR + radius_expr = SR(RR)(radius_expr) + return radius_expr.limit(x="oo") + return self._radius + + def path_distance_metric(self, other): + r""" + Returns the path distance metric distance between this point and ``other``. + + Also referred to as the hyperbolic metric, or the big metric. + + On the set of type II, III and IV points, the path distance metric + is a metric. Following Baker and Rumely, we extend + the path distance metric to type I points `x`, `y` by `\rho(x,x) = 0` and `\rho(x,y) = + \infty`. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: A finite or infinite real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(1/4, 4) + sage: Q2 = B(1/4, 6) + sage: Q1.path_distance_metric(Q2) + 0.369070246428542 + + :: + + sage: Q3 = B(1) + sage: Q3.path_distance_metric(Q1) + +infinity + + :: + + sage: Q3.path_distance_metric(Q3) + 0 + """ + if not isinstance(other, type(self)): + raise TypeError('other must be a point of Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError("other must be a point of the same Berkovich space") + if self.type_of_point() == 1 or other.type_of_point() == 1: + if self == other: + return 0 + else: + return RR(Infinity) + return 2*(self.join(other).diameter().log(self.prime()))\ + - self.diameter().log(self.prime())\ + - other.diameter().log(other.prime()) + + big_metric = path_distance_metric + + hyperbolic_metric = path_distance_metric + + def Hsia_kernel(self, other, basepoint): + """ + The Hsia kernel of this point and ``other``, + with basepoint ``basepoint``. + + The Hsia kernel with arbitrary basepoint + is a generalization of the Hsia kernel at infinity. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. + + OUTPUT: A finite or infinite real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2, 9) + sage: Q2 = B(1/27, 1/27) + sage: Q3 = B(1, 1/3) + sage: Q1.Hsia_kernel(Q2, Q3) + 0.111111111111111 + + :: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2, 9) + sage: Q2 = B(1/2) + sage: Q3 = B(1/2) + sage: Q1.Hsia_kernel(Q2, Q3) + +infinity + + """ + if not isinstance(other, type(self)): + raise TypeError('other must be a point of Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError("other must be a point of the same Berkovich space") + if not isinstance(basepoint, type(self)): + raise TypeError('basepoint must be a point of Berkovich space. basepoint was %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich space") + if basepoint.type_of_point() == 1: + if self == basepoint or other == basepoint: + return RR(Infinity) + return (self.spherical_kernel(other))/ \ + (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + + def small_metric(self, other): + r""" + Returns the small metric distance between this point and ``other``. + + The small metric is an extension of twice + the spherical distance on `P^1(\CC_p)`. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: A real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(1/4, 4) + sage: Q2 = B(1/4, 6) + sage: Q1.small_metric(Q2) + 0.0833333333333333 + + :: + + sage: B = Berkovich_Cp_Projective(QQ, 5) + sage: Q1 = B(0, 1) + sage: Q2 = B(99) + sage: Q1.small_metric(Q2) + 1.00000000000000 + + :: + + sage: Q3 = B(1/4, 4) + sage: Q3.small_metric(Q2) + 1.75000000000000 + + :: + + sage: Q2.small_metric(Q3) + 1.75000000000000 + """ + if not isinstance(other, Berkovich_Element_Cp): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same Berkovich space') + gauss = self.parent()(RR(0), RR(1)) + g_greater_than_s = gauss.partial_order(self) + g_greater_than_o = gauss.partial_order(other) + if g_greater_than_s and g_greater_than_o: + return 2*(self.join(other,gauss).diameter()) - self.diameter() - other.diameter() + if not g_greater_than_s: + new_self = self.involution_map() + else: + new_self = self + if not g_greater_than_o: + new_other = other.involution_map() + else: + new_other = other + return 2*(new_self.join(new_other,gauss).diameter()) \ + - new_self.diameter() - new_other.diameter() + + def Hsia_kernel_infinity(self, other): + r""" + Return the Hsia kernel at infinity of this point with ``other``. + + The Hsia kernel at infinity is the natural extension of the + absolute value on `\CC_p` to Berkovich space. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: A real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(1/4, 4) + sage: Q2 = B(1/4, 6) + sage: Q1.Hsia_kernel_infinity(Q2) + 6.00000000000000 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(4) + sage: Q2 = B(0, 1.5) + sage: Q1.Hsia_kernel_infinity(Q2) + 1.50000000000000 + """ + return self.join(other).diameter() + + def center(self): + r""" + Returns the center of the corresponding disk (or sequence of disks) + in `\CC_p`. + + OUTPUT: An element of the ``base`` of the parent Berkovich space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(3, 1).center() + 3 + O(3^21) + + :: + + sage: C = Berkovich_Cp_Projective(3) + sage: C(3, 1).center() + (3 + O(3^21) : 1 + O(3^20)) + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3 + 20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: B(a^2+4).center() + (a^2 + 4 : 1) + """ + if self._type == 4: + return self._center_lst + return self._center + + def type_of_point(self): + """ + Returns the type of this point of Berkovich space over ``Cp`` + + OUTPUT: An integer between 1 and 4 inclusive. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(1).type_of_point() + 1 + + :: + + sage: B(0, 1).type_of_point() + 2 + """ + return ZZ(self._type) + + def prime(self): + """ + The residue characteristic of the parent. + + OUTPUT: A prime integer. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(1).prime() + 3 + """ + return ZZ(self._p) + + def __ne__(self, other): + """ + Non-equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(3, 3**(1/2)) + sage: Q2 = B(3, RR(3**(1/2))) + sage: Q1 != Q2 + False + """ + return not (self == other) + + def _repr_(self): + """ + String representation of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: B(2, 1) + Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 + """ + if self._type == 1: + return "Type I point centered at " + format(self._center) + elif self._type == 2: + return "Type II point centered at " \ + + format(self._center) \ + + " of radius %s^%s" %(self._p, self._power) + elif self._type == 3: + return "Type III point centered at " \ + + format(self._center) + " of radius " \ + + format(self._radius) + else: + if self._center_func != None and self._radius_func != None: + return "Type IV point of precision %s " %self._prec + \ + "with centers given by %s and radii given by %s"\ + %(self._center_func, self._radius_func) + else: + return "Type IV point of precision %s, approximated " %self._prec + \ + "by disks centered at %s ... with radii %s ..." \ + %(self._center_lst[:min(self._prec, 2)], self._radius_lst[:min(self._prec, 2)]) + + def _latex_(self): + r""" + LaTeX representation of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: latex(B(2, 1)) + \text{type 2 Point of } \text{Projective Berkovich line over } + \Bold{C}_{3} \text{equivalent to the disk centered at + (2 + O(3^20) : 1 + O(3^20)) of radius 1.00000000000000 in } \Bold{C}_3 + """ + from sage.misc.latex import latex + if self._type == 1: + text = r"the point %s of } \Bold{C}_%s" %(self._center, self._p) + elif self._type in [2,3]: + text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" \ + %(self._center, self._radius, self._p) + else: + text = "the sequence of disks with centers %s } " %self._center_lst[:2] + \ + r"\ldots \text{ and radii %s } \ldots" %self._radius_lst[:2] + return r"\text{type %s Point of }" %(self._type) \ + + latex(self.parent()) + r"\text{equivalent to " + text + +class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): + r""" + Element class of the Berkovich affine line over `\CC_p`. + + Elements are categorized into four types, represented by specific data: + + - Type I points are represented by a center in the ``base`` of the parent Berkovich space, + which is `\QQ_p`, a finite extension of `\QQ_p`, or a number field. + + - Type II points are represented by a center in the ``base`` of the parent Berkovich space, + and a rational power of `p`. + + - Type III points are represented by a center in the ``base`` of the parent Berkovich space, + and a radius in `[0,\infty)`. + + - Type IV points are represented by a finite list of centers in the ``base`` of the parent + Berkovich space and a finite list of radii in `[0,\infty)`. Type IV points can be created + from univariate functions, allowing for arbitrary precision. + + INPUT: + + - ``center`` -- For type I, II, and III points, the center of the + corresponding disk in `\CC_p`. If the parent Berkovich space was created using a number field + `K`, then ``center`` must be an element of `K`. Otherwise, ``center`` must be an element of a + p-adic field. For type IV points, can be a list of centers used to approximate the point or a + univariate function that computes the centers (computation starts at 1). + + - ``radius`` -- (optional) For type I, II, and III points, the radius of the + corresponding disk in ``Cp``. Must coerce into the real numbers. For type IV points, + can be a list of radii used to approximate the point or a univariate function that + computes the radii (computation starts at 1). + + - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies + the power of ``p`` such that `p^\text{power}` = radius. + + - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point. + + - ``error_check`` -- (default: True) If error checking should be run on input. If + input is correctly formatted, can be set to ``False`` for better performance. + WARNING: with error check set to ``False``, any error in the input will lead to + incorrect results. + + EXAMPLES: + + Type I points can be created by specifying the corresponding point of ``Cp``:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: a = B(4) + sage: a + Type I point centered at 1 + 3 + O(3^20) + + The center of a point can be an element of a finite extension of ``Qp``:: + + sage: A. = Qq(27) + sage: a = B(1+t) + sage: a + Type I point centered at (t + 1) + O(3^20) + + Type II and III points can be created by specifying a center and a radius:: + + sage: b = B(2, 3**(1/2)); b + Type II point centered at 2 + O(3^20) of radius 3^1/2 + sage: c = B(2, 1.6); c + Type III point centered at 2 + O(3^20) of radius 1.60000000000000 + + Some type II points may be mistaken for type III points:: + + sage: b = B(3, 3**0.5); b #not tested + Type III point centered at 3 + O(3^21) of radius 1.73205080756888 + + To avoid these errors, specify the power instead of the radius:: + + sage: b = B(3, power=RR(1/100000)); b + Type II point centered at 3 + O(3^21) of radius 3^1/100000 + + Type IV points can be constructed in a number of ways, the first being + from a list of centers and radii used to approximate the point:: + + sage: d = B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]); d + Type IV point of precision 3, approximated by disks centered at + [2 + O(3^20), 2 + O(3^20)] ... with radii [1.76100000000000, 1.12300000000000] ... + + Type IV points can be constructed from univariate functions, with arbitrary precision:: + + sage: A. = Qq(27) + sage: R. = PolynomialRing(A) + sage: f = (1+t)^2*x + sage: S. = PolynomialRing(RR) + sage: S = FractionField(S) + sage: g = (y+1)/y + sage: d = B(f, g, prec=100); d + Type IV point of precision 100 with centers given by + ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y + + For increased performance, error_check can be set to ``False``. WARNING: with error check set + to ``False``, any error in the input will lead to incorrect results:: + + sage: d = B(f, g, prec=100,error_check=False); d + Type IV point of precision 100 with centers given by + ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y + + When creating a Berkovich space backed by a number field, points can be created similarily:: + + sage: R. = QQ[] + sage: A. = NumberField(x^3 + 20) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(a); Q1 + Type I point centered at (a : 1) + + :: + + sage: Q2 = B(a+1, 3); Q2 + Type II point centered at (a + 1 : 1) of radius 3^1 + + TESTS:: + + sage: A = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = A(3, 1); Q1 + Type II point centered at 3 + O(3^21) of radius 3^0 + + sage: Q2 = A(2.5, 1); Q2 + Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + + 3^18 + 3^19 + O(3^20) of radius 3^0 + + sage: Q5 = A(3, 0); Q5 + Type I point centered at 3 + O(3^21) + + sage: A(Zp(3)(2), 2).center().parent() == A(Qp(3)(2), 2).center().parent() + True + + sage: Q1 == Q2 + True + + sage: Q1 == Q5 + False + + sage: Q3 = A(Qp(3)(3), power=0, error_check=False); Q3 + Type II point centered at 3 + O(3^21) of radius 3^0 + + sage: Q4 = A(3, 3**0); Q4 + Type II point centered at 3 + O(3^21) of radius 3^0 + + sage: Q5 = A(3, power = 1/2); Q5 + Type II point centered at 3 + O(3^21) of radius 3^1/2 + + sage: Q6 = A(3, RR(3**(1/2))); Q6 + Type III point centered at 3 + O(3^21) of radius 1.73205080756888 + + sage: Q5 == Q6 + True + + sage: k = Qp(5) + sage: R. = k[] + sage: l. = k.extension(x^2 - 5) + sage: B = Berkovich_Cp_Affine(Qp(5)) + sage: B(w, power=1) + Type II point centered at w + O(w^41) of radius 5^1 + """ + def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): + #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space + #unless we are passed a point of projective Berkovich space + Element.__init__(self, parent) + self._p = parent.prime() + self._base_space = parent.base() + self._base_type = parent._base_type + self._ideal = parent._ideal + + #if this is a point of projective berkovich space, we raise an error + if isinstance(center, Berkovich_Element_Cp_Projective): + raise TypeError('use as_affine_point to convert to affine Berkovich space') + + Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, \ + prec=prec, space_type="affine", error_check=error_check) + + def as_projective_point(self): + r""" + Returns the corresponding point of projective Berkovich space. + + We identify affine Berkovich space with the subset `P^1_{\text{Berk}}(C_p) - (1 : 0)`. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(5) + sage: B(5).as_projective_point() + Type I point centered at (5 + O(5^21) : 1 + O(5^20)) + + :: + + sage: B(0, 1).as_projective_point() + Type II point centered at (0 : 1 + O(5^20)) of radius 5^0 + + :: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q2 = B(f, g) + sage: Q2.as_projective_point() + Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) + and radii given by 40.0000000000000*pi/x + """ + new_space = self.parent()._projective_space() + if self.type_of_point() == 1: + return new_space(self.center()) + elif self.type_of_point() == 2: + return new_space(self.center(), power=self.power()) + elif self.type_of_point() == 3: + return new_space(self.center(), self.radius()) + if self._center_func == None: + center = self.center() + else: + center = self.center_function() + if self._radius_func == None: + radius = self.radius() + else: + radius = self.radius_function() + return new_space(center, radius, prec=self.prec()) + + def center(self): + """ + Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. + + OUTPUT: + + - For type I-III points, a point of ``Cp``. + - For type IV points, a list of points of ``Cp``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(2, 1) + sage: Q1.center() + 2 + O(3^20) + + :: + + sage: d = B([4, 2], [4, 2]) + sage: d.center() + [1 + 3 + O(3^20), 2 + O(3^20)] + """ + return super().center() + + def __eq__(self, other): + """ + Equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(1, RR(3**(1/2))) + sage: Q2 = B(1, 3**(1/2)) + sage: Q1 == Q2 + True + + :: + + sage: Q3 = B(1) + sage: Q4 = B(4) + sage: Q3 == Q4 + False + + :: + + sage: Q5 = B(1, 4) + sage: Q1 == Q5 + False + + :: + + sage: Q1 == Q3 + False + """ + if other is self: + return True + if not isinstance(other, Berkovich_Element_Cp_Affine): + return False + if other.parent() != self.parent(): + return False + stype = self.type_of_point() + otype = other.type_of_point() + if stype == otype and stype == 1: + return self.center() == other.center() + elif stype == otype and stype == 4: + raise NotImplementedError("Equality for type IV points not yet implemented") + elif stype in [2,3] and otype in [2,3]: + if self.radius() != other.radius(): + return False + center_dist = self._custom_abs(self.center() - other.center()) + return center_dist <= self.radius() + else: + return False + + def __hash__(self): + """ + Returns the hash of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1, RR(3**(1/2))) + sage: Q2 = B(1, 3**(1/2)) + sage: hash(Q1) == hash(Q2) + True + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(a^2+1, 2) + sage: Q2 = B(0, 2) + sage: hash(Q1) == hash(Q2) + True + """ + if self.type_of_point() == 1: + return hash(str(self.center())) + elif self.type_of_point() == 4: + raise NotImplementedError('hash not defined for type IV points') + return hash(str(self.radius())) + + def partial_order(self,other): + r""" + The standard partial order on Berkovich space. + + Roughly, the partial order corresponds to containment of + the corresponding disks in `\CC_p`. + + For example, let x and y be points of type II or III. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2)` in `\CC_p`. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: + + - ``True`` -- If self > other in the standard partial order. + - ``False`` -- If self < other in the standard partial order. + - ``None`` -- If the two points are not comparable. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2, 4) + sage: Q2 = B(2, 6) + sage: Q1.partial_order(Q2) + False + + :: + + sage: Q3 = B(1/2) + sage: Q1.partial_order(Q3) + True + + :: + + sage: Q4 = B(1/81, 1) + sage: Q4.partial_order(Q1) is None + True + + :: + + sage: Q4.partial_order(Q3) is None + True + """ + #error check, then convert to projective berkovich space to do the partial order + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same affine Berkovich space') + return self.as_projective_point().partial_order(other.as_projective_point()) + + def join(self, other, basepoint=Infinity): + """ + Computes the join of this point and ``other`` with respect to ``basepoint``. + + The join is first point that lies on the interesection + of the path from this point to ``basepoint`` and the path from ``other`` to + ``basepoint``. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- (default: Infinity) A point of the same + Berkovich space as this point or Infinity. + + OUTPUT: A point of the same Berkovich space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 2) + sage: Q1.join(Q2) + Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + + :: + + sage: Q3 = B(5) + sage: Q3.join(Q1) + Type II point centered at 2 + 3 + O(3^20) of radius 3^0 + + :: + + sage: Q3.join(Q1, basepoint=Q2) + Type II point centered at 2 + O(3^20) of radius 3^0 + + TESTS:: + + sage: Q4 = B(1/3**8 + 2, 1) + sage: Q2.join(Q4, basepoint = Q1) + Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + + :: + + sage: Q5 = B(2, 1/9) + sage: Q6 = B(1, 1/27) + sage: Q4.join(Q5, basepoint=Q6) + Type II point centered at 1 + O(3^20) of radius 3^0 + + :: + + sage: Q7 = B(1/27, 1/27) + sage: Q1.join(Q7, Q2) + Type III point centered at 2 + O(3^20) of radius 2.00000000000000 + """ + #we error check and then pass to projective space to do the join + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same affine Berkovich space') + if self.type_of_point() == 4 or other.type_of_point() == 4: + raise NotImplementedError("join with type IV points not implemented") + + proj_self = self.as_projective_point() + proj_other = other.as_projective_point() + + if basepoint == Infinity: + return proj_self.join(proj_other).as_affine_point() + + if not isinstance(basepoint, Berkovich_Element_Cp_Affine): + raise TypeError('basepoint must a point of affine Berkovich space. basepoint was %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same affine Berkovich space") + if basepoint.type_of_point() == 4: + raise NotImplementedError("join not implemented for type IV basepoint") + proj_basepoint = basepoint.as_projective_point() + return proj_self.join(proj_other, proj_basepoint).as_affine_point() + + + def involution_map(self): + r""" + Returns the image of this point under the involution map. + + The involution map is the extension of the map ``z |-> 1/z`` + on `\CC_p` to Berkovich space. + + For Affine Berkovich Space, not defined for the type I + point centered at 0. + + OUTPUT: A point of the same Berkovich space. + + EXAMPLES: + + The involution map is 1/z on type I points:: + + sage: B = Berkovich_Cp_Affine((3)) + sage: Q1 = B(1/2) + sage: Q1.involution_map() + Type I point centered at 2 + O(3^20) + + :: + + sage: Q2 = B(0, 1/3) + sage: Q2.involution_map() + Type II point centered at 0 of radius 3^1 + + :: + + sage: Q3 = B(1/3, 1/3) + sage: Q3.involution_map() + Type II point centered at 3 + O(3^21) of radius 3^-3 + + """ + if self.type_of_point() == 1: + if self.center() == 0: + raise ValueError("involution map not deffined on affine type I point centered at 0") + return self.parent()(1/self.center()) + + zero = self.parent()(QQ(0)) + radius = self.radius() + + if self.type_of_point() in [2,3]: + zero_contained_in_self = self.partial_order(zero) + if zero_contained_in_self: + if self.type_of_point() == 2: + power = self.power() + return self.parent()(0, power=-power) + return self.parent()(0, RR(1/radius)) + return self.parent()(1/self.center(), RR( radius / (self._custom_abs(self.center())**2) ) ) + + new_center_lst = [] + new_radius_lst = [] + for i in range(len(center)): + berk_point = self.parent()(self.center()[i], radius[i]) + zero_check = berk_point.partial_order(zero) + if zero_check: + new_center = 0 + new_radius = RR(1/radius[i]) + else: + new_center = 1/self.center()[i] + new_radius = RR(radius[i] / (self._custom_abs(self.center()[i])**2)) + new_center_lst.append(new_center) + new_radius_lst.append(new_radius) + return self.parent()(new_center_lst, new_radius_lst, error_check=False) + + def contained_in_interval(self, start, end): + """ + Checks if this point is an element of the interval [``start``, ``end``]. + + INPUT: + + - ``start`` -- A point of the same Berkovich space as this point. + - ``end`` -- A point of the same Berkovich space as this point. + + OUTPUT: + + - ``True`` if this point is an element of [``start``, ``end``]. + - ``False`` otherwise. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective((3)) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 4) + sage: Q3 = B(1/3) + sage: Q2.contained_in_interval(Q1, Q3.join(Q1)) + False + + :: + + sage: Q4 = B(1/81, 1) + sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) + True + """ + if not isinstance(start, Berkovich_Element_Cp_Affine): + raise TypeError("start must be a point of affine Berkovich space. start was %s" %start) + if start.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + if not isinstance(end, Berkovich_Element_Cp_Affine): + raise TypeError("end must be a point of affine Berkovich space. end was %s" %end) + if end.parent() != self.parent(): + raise ValueError("end must be a point of the same Berkovich space as this point") + + proj_self = self.as_projective_point() + proj_start = start.as_projective_point() + proj_end = end.as_projective_point() + return proj_self.contained_in_interval(proj_start, proj_end) + + def potential_kernel(self, other, basepoint): + """ + The potential kernel of this point with ``other`` with basepoint ``basepoint``. + + The potential kernel is the hyperbolic distance between ``basepoint`` and the join + of this point with ``other`` relative to ``basepoint``. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. + + OUTPUT: A finite or infinite real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(27, 1) + sage: Q2 = B(1/3, 2) + sage: Q3 = B(1/9, 1/2) + sage: Q3.potential_kernel(Q1, Q2) + 0.369070246428543 + """ + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same affine Berkovich space') + if not isinstance(basepoint, Berkovich_Element_Cp_Affine): + raise TypeError('basepoint must be a point of affine Berkovich space. basepoint was %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError('basepoint must be a point of the same affine Berkovich space') + return basepoint.path_distance_metric(self.join(other, basepoint)) + + def spherical_kernel(self,other): + r""" + The spherical kernel of this point with ``other``. + + The spherical kernel is one possible extension of + the spherical distance on `A^1(\CC_p)` to the Berkovich + Affine line. + + OUTPUT: A real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: Q1 = B(2, 9) + sage: Q2 = B(1/27, 1/27) + sage: Q1.spherical_kernel(Q2) + 0.111111111111111 + + """ + if not isinstance(other,Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of an affine Berkovich space. other was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other was not a point of the same affine Berkovich space') + gauss_point = self.parent()(RR(0), RR(1)) + w = self.join(other,gauss_point) + dist = gauss_point.path_distance_metric(w) + if dist == Infinity: + return 0 + return (self.prime())**(-1*dist) + + def diameter(self, basepoint=Infinity): + r""" + Generalized diameter function. + + If the basepoint is infinity, the diameter is equal to + the limit of the radii of the corresponding disks in `\CC_p`. + + If the basepoint is not infinity, the diameter + is the Hsia kernel of this point with itself at + basepoint ``basepoint``. + + INPUT: + + - ``basepoint`` -- (default = Infinity) A point of the + same Berkovich space as this point. + + OUTPUT: A finite or infinite real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1/81, 1) + sage: Q2 = B(1/3) + sage: Q1.diameter(Q2) + 0.00137174211248285 + + :: + + sage: Q2.diameter(Q2) + +infinity + """ + if basepoint == Infinity: + return super().diameter() + return self.Hsia_kernel(self, basepoint) + +class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): + r""" + Element class of the Berkovich projective line over `\CC_p`. + + Elements are categorized into four types, represented by specific data: + + - Type I points are represented by a center in the ``base`` of the parent Berkovich space, + which is projective space of dimension 1 over either `\QQ_p`, a finite extension of `\QQ_p`, + or a number field. + + - Type II points are represented by a center in the ``base`` of the parent Berkovich space, + and a rational power of `p`. + + - Type III points are represented by a center in the ``base`` of the parent Berkovich space, + and by a radius, a real number, in `[0,\infty)`. + + - Type IV points are represented by a finite list of centers in the ``base`` of the parent + Berkovich space and a finite list of radii in `[0,\infty)`. + + The projective Berkovich line is viewed as the one-point compactification of + the affine Berkovich line. The projective Berkovich line therefore contains + every point of the affine Berkovich line, along with a type I point centered + at infinity. + + INPUT: + + - ``center`` -- For type I, II, and III points, the center of the + corresponding disk in `P^1(\CC_p)`. If the parent Berkovich space was created using a number field + `K`, then ``center`` can be an element of `P^1(K)`. Otherwise, ``center`` + must be an element of a projective space of dimension 1 over a padic field. + For type IV points, can be a list of centers used to approximate the point or a + univariate function that computes the centers (computation starts at 1). + + - ``radius`` -- (optional) For type I, II, and III points, the radius of the + corresponding disk in `\CC_p`. Must coerce into the real numbers. For type IV points, + can be a list of radii used to approximate the point or a univariate function that + computes the radii (computation starts at 1). + + - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies + the power of ``p`` such that `p^\text{power}` = radius + + - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point + + - ``error_check`` -- (default: True) If error checking should be run on input. If + input is correctly formatted, can be set to ``False`` for better performance. + WARNING: with error check set to ``False``, any error in the input will lead to + incorrect results. + + EXAMPLES: + + Type I points can be created by specifying the corresponding point of `P^1(\CC_p)`:: + + sage: S = ProjectiveSpace(Qp(5), 1) + sage: P = Berkovich_Cp_Projective(S); P + Projective Berkovich line over Cp(5) of precision 20 + + sage: a = S(0,1) + sage: Q1 = P(a); Q1 + Type I point centered at (0 : 1 + O(5^20)) + + sage: Q2 = P((1,0)); Q2 + Type I point centered at (1 + O(5^20) : 0) + + Type II and III points can be created by specifying a center and a radius:: + + sage: Q3 = P((0,5), 5**(3/2)); Q3 + Type II point centered at (0 : 1 + O(5^20)) of radius 5^3/2 + + sage: Q4 = P(0, 3**(3/2)); Q4 + Type III point centered at (0 : 1 + O(5^20)) of radius 5.19615242270663 + + Type IV points can be created from lists of centers and radii:: + + sage: b = S((3,2)) #create centers + sage: c = S((4,3)) + sage: d = S((2,3)) + sage: L = [b, c, d] + sage: R = [1.761, 1.123, 1.112] + sage: Q5 = P(L, R); Q5 + Type IV point of precision 3, approximated by disks centered at + [(4 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + + 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20) : + 1 + O(5^20)), (3 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + + 5^10 + 3*5^11 + 5^12 + 3*5^13 + 5^14 + 3*5^15 + 5^16 + 3*5^17 + 5^18 + 3*5^19 + O(5^20) : + 1 + O(5^20))] ... with radii [1.76100000000000, 1.12300000000000] ... + + Type IV points can also be created from univariate functions. Since the centers of + the sequence of disks can not be the point at infinity in `P^1(\CC_p)`, only functions + into `\CC_p` are supported:: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q6 = P(f, g); Q6 + Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) + and radii given by 40.0000000000000*pi/x + + TESTS:: + + sage: P((1,0), 3) + Traceback (most recent call last): + ... + ValueError: type II and III points can not be centered at infinity + + """ + def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): + #if we are given a point of Affine Berkovich Space, we do the conversion + #otherwise we call the Berkovich_Element_Cp constructor with space_type="projective" + + Element.__init__(self, parent) + self._p = parent.prime() + self._base_space = parent.base() + self._base_type = parent._base_type + self._ideal = parent._ideal + + #conversion from Affine points is handled in this constructor + if isinstance(center, Berkovich_Element_Cp_Affine): + raise TypeError('use as_projective_point to convert to projective Berkovich space') + + Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ + prec=prec,space_type="projective",error_check=error_check) + + def as_affine_point(self): + """ + Returns the corresponding affine point after dehomogenizing at infinity. + + OUTPUT: A point of affine Berkovich space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(5) + sage: B(5).as_affine_point() + Type I point centered at 5 + O(5^21) + + :: + + sage: B(0, 1).as_affine_point() + Type II point centered at 0 of radius 5^0 + + :: + + sage: L. = PolynomialRing(Qp(5)) + sage: T = FractionField(L) + sage: f = T(1/t) + sage: R. = RR[] + sage: Y = FractionField(R) + sage: g = (40*pi)/x + sage: Q2 = B(f, g) + sage: Q2.as_affine_point() + Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) + and radii given by 40.0000000000000*pi/x + """ + if self.center()[1] == 0: + raise ValueError('cannot convert infinity to affine Berkovich space') + new_space = self.parent()._affine_space() + if self.type_of_point() in [1,2,3]: + center = self.center()[0] + if self.type_of_point() == 1: + return new_space(center) + elif self.type_of_point() == 2: + return new_space(center, power=self.power()) + elif self.type_of_point() == 3: + return new_space(center, self.radius()) + if self._center_func == None: + center = [i[0] for i in self.center()] + else: + center = self.center_function() + if self._radius_func == None: + radius = self.radius() + else: + radius = self.radius_function() + return new_space(center, radius, prec=self.prec()) + + def center(self): + r""" + Returns the center of the corresponding disk (or sequence of disks) in `P^1(\CC_p)`. + + OUTPUT: + + - For type I-III points, a point of `P^1(\CC_p)`. + - For type IV points, a list of points of `P^1(\CC_p)`. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2, 1) + sage: Q1.center() + (2 + O(3^20) : 1 + O(3^20)) + + :: + + sage: d = B([4, 2], [4, 2]) + sage: d.center() + [(1 + 3 + O(3^20) : 1 + O(3^20)), (2 + O(3^20) : 1 + O(3^20))] + """ + return super().center() + + def __eq__(self, other): + """ + Equality operator. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B([2, 2], RR(3**(1/2))) + sage: Q2 = B([1, 1], 3**(1/2)) + sage: Q1 == Q2 + True + + :: + + sage: Q3 = B(1) + sage: Q4 = B(4) + sage: Q3 == Q4 + False + + :: + + sage: Q5 = B(1, 4) + sage: Q1 == Q5 + False + + :: + + sage: Q1 == Q3 + False + """ + if other is self: + return True + if not isinstance(other, Berkovich_Element_Cp_Projective): + return False + if other.parent() != self.parent(): + return False + stype = self.type_of_point() + otype = other.type_of_point() + if stype == otype and stype == 1: + return self.center() == other.center() + elif stype == otype and stype == 4: + raise NotImplementedError("equality for type IV points not implemented") + elif stype in [2,3] and otype in [2,3]: + if self.radius() != other.radius(): + return False + scent = self.center()[0] + ocent = other.center()[0] + center_dist = self._custom_abs(scent - ocent) + return center_dist <= self.radius() + else: + return False + + def __hash__(self): + """ + Returns the hash of this point. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: P = ProjectiveSpace(B.base_ring(), 1) + sage: Q1 = B(P.point([2,2], False), RR(3**(1/2))) + sage: Q2 = B([1,1], 3**(1/2)) + sage: hash(Q1) == hash(Q2) + True + + :: + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.ideal(-1/2*a^2 + a - 3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: Q1 = B(a^2+1, 2) + sage: Q2 = B(0, 2) + sage: hash(Q1) == hash(Q2) + True + """ + if self.type_of_point() == 1: + return hash(str(self.center())) + elif self.type_of_point() == 4: + raise ValueError('hash not defined for type IV points') + return hash(str(self.radius())) + + def partial_order(self,other): + r""" + The standard partial order on Berkovich space. + + Roughly, the partial order corresponds to containment of + the corresponding disks in ``Cp``. + + For example, let x and y be points of type II or III. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2)` in `\CC_p`. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: + + - ``True`` - If self => other in the standard partial order. + - ``False`` - If other > self in the standard partial order. + - ``None`` - If the two points are not comparable. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) + sage: Q1 = B(2, 4) + sage: Q2 = B(2, 6) + sage: Q1.partial_order(Q2) + False + + :: + + sage: Q3 = B(1/2) + sage: Q1.partial_order(Q3) + True + + :: + + sage: Q4 = B(1/81, 1) + sage: Q4.partial_order(Q1) == None + True + + We check infinity works in the partial order:: + + sage: Q5 = B((1,0)) + sage: Q6 = B(3, 3) + sage: Q6.partial_order(Q5) + True + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B((1,0)) + sage: Q2 = B(0) + sage: Q1.partial_order(Q2) == None + True + + :: + + sage: Q2.partial_order(Q2) + True + + :: + + sage: Q3 = B(2) + sage: Q2.partial_order(Q3) == None + True + + :: + + sage: Q4 = B(1/27, 1) + sage: Q4.partial_order(Q2) == None + True + + :: + + sage: Q2.partial_order(Q4) == None + True + + :: + + sage: Q5 = B(0,1) + sage: Q2.partial_order(Q5) + False + + :: + + sage: Q6 = B([3,2],[4,1/27]) + sage: Q4.partial_order(Q6) == None + True + + :: + + sage: Q5.partial_order(Q6) + True + + :: + + sage: Q7 = B(0,1/27) + sage: Q5.partial_order(Q7) + True + + :: + + sage: Q8 = B(0, 2) + sage: Q5.partial_order(Q8) + False + + :: + + sage: Q9 = B(1/9,10) + sage: Q5.partial_order(Q9) + False + """ + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same projective Berkovich space') + + #if self or other is infinity, we apply the involution map + infty = self.parent()((1,0)) + zero = self.parent()(0) + if self == infty or other == infty: + if self == zero or other == zero: + return None + newself = self.involution_map() + newother = other.involution_map() + return newself.partial_order(newother) + + if self.type_of_point() == 1: + if other.type_of_point() in [1,4]: + if self == other: + return True + return None + else: + s_less_than_o = other.partial_order(self) + if s_less_than_o == None: + return None + return not s_less_than_o + else: + if other.type_of_point() == 1: + dist = self._custom_abs(self.center()[0] - other.center()[0]) + if dist <= self.radius(): + return True + return None + elif other.type_of_point() == 4: + center = other.center()[-1] + dist = self._custom_abs(self.center()[0] - center[0]) + if dist <= self.radius() and other.radius()[-1] <= self.radius(): + return True + return None + else: + dist = self._custom_abs(self.center()[0]-other.center()[0]) + if dist <= self.radius(): + if other.radius() <= self.radius(): + return True + return False + else: + if dist <= other.radius(): + return False + return None + + def join(self, other, basepoint=Infinity): + """ + Computes the join of this point and ``other``, with respect to ``basepoint``. + + The join is first point that lies on the interesection + of the path from this point to ``basepoint`` and the path from ``other`` to + ``basepoint``. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- (default: Infinity) A point of the same + Berkovich space as this point, or infinity. + + OUTPUT: A point of the same Berkovich space. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 2) + sage: Q1.join(Q2) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + :: + + sage: Q3 = B(5) + sage: Q3.join(Q1) + Type II point centered at (2 + 3 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + :: + + sage: Q3.join(Q1, basepoint=Q2) + Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + TESTS:: + + sage: Q4 = B(1/3**8+2, 1) + sage: Q2.join(Q4, basepoint=Q1) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q5 = B(2, 1/9) + sage: Q6 = B(1, 1/27) + sage: Q4.join(Q5, basepoint=Q6) + Type II point centered at (1 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + sage: Q7 = B(1/27, 1/27) + sage: Q1.join(Q7, Q2) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q1.join(Q2, Q7) + Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 + + sage: Q8 = B(0, power=1/3) + sage: Q9 = B(0, power=1/2) + sage: Q8.join(Q9) + Type II point centered at (0 : 1 + O(3^20)) of radius 3^1/2 + + sage: R. = QQ[] + sage: A. = NumberField(x^3+20) + sage: ideal = A.prime_above(3) + sage: C = Berkovich_Cp_Projective(A, ideal) + sage: Q10 = C(a, 1/9) + sage: Q10.join(Q9) + Traceback (most recent call last): + ... + ValueError: other must be a point of the same projective Berkovich line + + sage: Q11 = C(0, 1/3) + sage: Q11.join(Q10) + Type II point centered at (0 : 1) of radius 3^0 + """ + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same projective Berkovich line') + + #if either self or other is type IV, we use the last disk in the approximation + if self.type_of_point() == 4: + new_center = self.center()[-1] + new_radius = self.radius()[-1] + return self.parent()(new_center,new_radius).join(other) + if other.type_of_point() == 4: + new_center = other.center()[-1] + new_radius = other.radius()[-1] + return self.join(self.parent()(new_center,new_radius)) + + #we deal with the point at infinity as a special case + infty = self.parent()((1,0)) + + if basepoint == Infinity or basepoint == infty: + if self == infty or other == infty: + return infty + dist = self._custom_abs(self.center()[0] - other.center()[0]) + maximum = max(dist, self.radius(), other.radius()) + #optimize for when self or other are type II + if maximum == self.radius() and self.type_of_point() == 2: + return self.parent()(self.center(), power=self.power()) + if maximum == other.radius() and other.type_of_point() == 2: + return self.parent()(self.center(), power=other.power()) + return self.parent()(self.center(), maximum) + + if not isinstance(basepoint, Berkovich_Element_Cp_Projective): + raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError("basepoint must be a point of the same Berkovich projective line") + + #if the basepoint is type IV, we use the last disk in the approximation + if basepoint.type_of_point() == 4: + new_center = other.center()[-1] + new_radius = other.radius()[-1] + return self.join(other, self.parent()(new_center,new_radius)) + + if self == infty: + return other.join(basepoint) + if other == infty: + return self.join(basepoint) + + #since none of the self, other, and basepoint are infinity, we can now treat them + #as affine points + b_greater_than_s = basepoint.partial_order(self) + b_greater_than_o = basepoint.partial_order(other) + s_greater_than_o = self.partial_order(other) + + #we deal with all the cases where self and other are not comparable first + if s_greater_than_o == None: + if b_greater_than_o == None: + if b_greater_than_s == None: + #case where none of the points are comparable + dist_b_s = self._custom_abs(self.center()[0] - basepoint.center()[0]) + dist_b_o = self._custom_abs(other.center()[0] - basepoint.center()[0]) + return self.parent()(basepoint.center(),\ + min(max(dist_b_o,other.radius(),basepoint.radius()), \ + max(dist_b_s,self.radius(),basepoint.radius()))) + + #case where self and basepoint are comparable + else: + if b_greater_than_s: + return basepoint + else: + return self + + #case where other and basepoint are comparable + else: + if b_greater_than_o: + return basepoint + else: + return other + + #now the cases where self > other + elif s_greater_than_o: + if b_greater_than_s == None: + return self + if b_greater_than_s: + return self + if b_greater_than_o: + return basepoint + if b_greater_than_o == False: + return other + + #join is symmetric, so we flip self and other so that self > other + else: + return other.join(self,basepoint) + + def involution_map(self): + r""" + Returns the image of this point under the involution map. + + The involution map is the extension of the map ``z |-> 1/z`` + on `P^1(\CC_p)` to Berkovich space. + + OUTPUT: A point of the same Berkovich space. + + EXAMPLES: + + The involution map is 1/z on type I points:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(1/2) + sage: Q1.involution_map() + Type I point centered at (2 + O(3^20) : 1 + O(3^20)) + + :: + + sage: Q2 = B(0, 1/3) + sage: Q2.involution_map() + Type II point centered at (0 : 1 + O(3^20)) of radius 3^1 + + :: + + sage: Q3 = B(1/3, 1/3) + sage: Q3.involution_map() + Type II point centered at (3 + O(3^21) : 1 + O(3^20)) of radius 3^-3 + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B((1,0)).involution_map() + Type I point centered at (0 : 1 + O(3^20)) + + :: + + sage: B(0).involution_map() + Type I point centered at (1 + O(3^20) : 0) + + :: + + sage: B(1/81, 1.5).involution_map() + Type III point centered at (3^4 + O(3^24) : 1 + O(3^20)) of radius 0.000228623685413809 + """ + infty = self.parent()((1,0)) + zero = self.parent()(0) + + if self.type_of_point() == 1: + if self == infty: + return zero + if self == zero: + return infty + return self.parent()(1/self.center()[0]) + + if self.type_of_point() in [2,3]: + zero_contained_in_self = self.partial_order(zero) + if zero_contained_in_self: + if self.type_of_point() == 2: + power = self.power() + return self.parent()(0, power=-power) + return self.parent()(0, 1/self.radius()) + return self.parent()(1/self.center()[0], self.radius()/(self._custom_abs(self.center()[0])**2)) + + new_center_lst = [] + new_radius_lst = [] + for i in range(len(self.center())): + berk_point = self.parent()(self.center()[i], self.radius()[i]) + zero_check = berk_point.partial_order(zero) + if zero_check: + new_center = 0 + new_radius = 1/self.radius()[i] + else: + new_center = 1/self.center()[i][0] + new_radius = self.radius()[i]/(self._custom_abs(self.center()[i][0])**2) + new_center_lst.append(new_center) + new_radius_lst.append(new_radius) + return self.parent()(new_center_lst, new_radius_lst) + + def contained_in_interval(self, start, end): + """ + Checks if this point is an element of the interval [``start``, ``end``]. + + INPUT: + + - ``start`` -- A point of the same Berkovich space as this point. + - ``end`` -- A point of the same Berkovich space as this point. + + OUTPUT: + + - ``True`` if this point is an element of [``start``, ``end``]. + - ``False`` otherwise. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2, 1) + sage: Q2 = B(2, 4) + sage: Q3 = B(1/3) + sage: Q2.contained_in_interval(Q1, Q3.join(Q1)) + False + + :: + + sage: Q4 = B(1/81, 1) + sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) + True + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: infty = B((1,0)) + sage: zero = B(0) + sage: gauss = B(0,1) + sage: infty.contained_in_interval(zero, gauss) + False + + :: + + sage: Q1 = B(1,3) + sage: infty.contained_in_interval(gauss, Q1) + False + + :: + + sage: zero.contained_in_interval(infty, gauss) + False + + :: + + sage: gauss.contained_in_interval(zero, infty) + True + + :: + + sage: Q2 = B(81, 1/3) + sage: gauss.contained_in_interval(infty, Q2) + True + """ + if not isinstance(start, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if start.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + if not isinstance(end, Berkovich_Element_Cp): + raise ValueError("start must be a point of Berkovich space") + if end.parent() != self.parent(): + raise ValueError("start must be a point of the same Berkovich space as this point") + + #we treat infinity as a special case + infty = self.parent()((1, 0)) + zero = self.parent()(ZZ(0)) + if self == infty: + if start == zero or end == zero: + return end == infty or start == infty + return (self.involution_map()).contained_in_interval(start.involution_map(),\ + end.involution_map()) + if start == infty or end == infty: + if self == zero: + return end == zero or start == zero + if start == zero or end == zero: + gauss = self.parent()(ZZ(0),ZZ(1)) + return self.contained_in_interval(start,gauss) or self.contained_in_interval(gauss,end) + return self.involution_map().contained_in_interval(start.involution_map(), \ + end.involution_map()) + join = start.join(end) + return join.partial_order(self) and (self.partial_order(start) \ + or self.partial_order(end)) + + def potential_kernel(self, other, basepoint): + """ + The potential kernel of this point with ``other``, + with basepoint ``basepoint``. + + The potential kernel is the hyperbolic distance + between ``basepoint`` and the join of this point + with ``other`` relative to ``basepoint``. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. + + OUTPUT: A finite or infinite real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(27, 1) + sage: Q2 = B(1/3, 2) + sage: Q3 = B(1/9, 1/2) + sage: Q3.potential_kernel(Q1, Q2) + 0.369070246428543 + """ + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same projective Berkovich line') + if not isinstance(basepoint, Berkovich_Element_Cp_Projective): + raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError('basepoint must be a point of the same projective Berkovich line') + return basepoint.path_distance_metric(self.join(other, basepoint)) + + def spherical_kernel(self,other): + r""" + The spherical kernel of this point with ``other``. + + The spherical kernel is one possible extension of the spherical + distance on `P^1(\CC_p)` to the projective Berkovich line. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: A real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2, 2) + sage: Q2 = B(1/9, 1) + sage: Q1.spherical_kernel(Q2) + 0.500000000000000 + + :: + + sage: Q3 = B(2) + sage: Q3.spherical_kernel(Q3) + 0 + """ + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same projective Berkovich line') + gauss_point = self.parent()(ZZ(0), ZZ(1)) + w = self.join(other,gauss_point) + dist = gauss_point.path_distance_metric(w) + if dist == Infinity: + return 0 + return self.prime()**(-1*dist) + + def diameter(self, basepoint=Infinity): + r""" + Generalized diameter function. + + If the basepoint is infinity, the diameter is equal to + the limit of the radii of the corresponding disks in `\CC_p`. + + If the basepoint is not infinity, the diameter + is the Hsia kernel of this point with itself at + basepoint ``basepoint``. + + INPUT: + + - ``basepoint`` -- (default = Infinity) A point of the same + Berkovich space as this point, or infinity. + + OUTPUT: A real number or infinity. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(1/81, 1) + sage: Q2 = B(1/3) + sage: Q1.diameter(Q2) + 0.00137174211248285 + + :: + + sage: Q2.diameter(Q2) + +infinity + """ + if basepoint == Infinity: + return super().diameter() + else: + return self.Hsia_kernel(self, basepoint) \ No newline at end of file diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 60f2615bf5b..69f99229dd8 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -28,2510 +28,40 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.schemes.berkovich.berkovich_cp_element import (Berkovich_Element_Cp_Affine, + Berkovich_Element_Cp_Projective) from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.element import Element -from sage.categories.number_fields import NumberFields -from sage.categories.topological_spaces import TopologicalSpaces -from sage.symbolic.expression import is_Expression -from sage.rings.real_mpfr import RR, is_RealNumber -from sage.rings.padics.padic_generic_element import pAdicGenericElement -from sage.rings.padics.padic_base_generic import pAdicBaseGeneric -from sage.rings.padics.generic_nodes import is_pAdicField -from sage.rings.padics.factory import Qp -from sage.schemes.projective.projective_space import is_ProjectiveSpace, ProjectiveSpace -from sage.schemes.projective.projective_point import SchemeMorphism_point_projective_field -from sage.schemes.generic.morphism import is_SchemeMorphism -from sage.schemes.affine.affine_space import AffineSpace -from sage.schemes.generic.scheme import Scheme -from sage.rings.rational_field import QQ -from sage.rings.integer_ring import ZZ -from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal -from sage.rings.infinity import Infinity - -def is_Berkovich(space): - """ - Checks if ``space`` is a Berkovich space. - - OUTPUT: - - - ``True`` if ``space`` is a Berkovich space. - - ``False`` otherwise. - """ - return isinstance(space, Berkovich) - -def is_Berkovich_Cp(space): - """ - Checks if ``space`` is a Berkovich space over ``Cp``. - - OUTPUT: - - - ``True`` if ``space`` is a Berkovich space over ``Cp``. - - ``False`` otherwise. - """ - return isinstance(space, Berkovich_Cp) - - -class Berkovich_Element(Element): - """ - The parent class for any element of a Berkovich space - """ - pass - -class Berkovich_Element_Cp(Berkovich_Element): - r""" - The abstract parent class for any element of Berkovich space over `\CC_p`. - This class should never be instantiated, instead use :class:`Berkovich_Element_Cp_Affine` - or :class:`Berkovich_Element_Cp_Projective`. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B(2) - Type I point centered at 2 + O(3^20) - - :: - - sage: B(0, 1) - Type II point centered at 0 of radius 3^0 - """ - - def __init__(self, parent, center, radius=None, power=None, prec=20, space_type=None, error_check=True): - """ - Initialization function. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(5) - sage: B(4) - Type I point centered at 4 + O(5^20) - """ - from sage.rings.function_field.element import is_FunctionFieldElement - from sage.rings.polynomial.polynomial_element import is_Polynomial - from sage.rings.fraction_field_element import FractionFieldElement_1poly_field - self._type = None - - #if radius is a list or a tuple, this is a type 4 point - if isinstance(radius, list) or isinstance(radius, tuple): - if error_check: - if not (isinstance(center, list) or isinstance(center, tuple)): - raise TypeError("center was passed a list but radius was not a list") - if len(radius) != len(center): - raise ValueError("the same number of centers and radii must be specified to create " + \ - "a type IV point") - self._center_lst = list(center) - self._radius_lst = list(radius) - self._prec = len(self._radius_lst) - self._center_func = None - self._radius_func = None - self._type = 4 - self._radius = None - self._center = None - if not error_check: - return - - #is_FunctionFieldElement calls .parent - elif hasattr(center, "parent") and hasattr(radius, 'parent'): - from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial - if is_MPolynomial(center): - try: - center = center.univariate_polynomial() - except: - raise TypeError('center was %s, a multivariable polynomial' %center) - - #check if the radius and the center are functions - center_func_check = is_FunctionFieldElement(center) or is_Polynomial(center) or\ - isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center) - radius_func_check = is_FunctionFieldElement(radius) or is_Polynomial(radius) or\ - isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius) - - if center_func_check: - #check that both center and radii are supported univariate function - center_expr_check = False - radius_expr_check = False - if error_check: - if is_Expression(center): - if len(center.variables()) != 1: - raise ValueError("an expression with %s " %(len(center.variables())) + \ - "variables cannot define the centers approximating a type IV point") - else: - #we do this since .subs is currently buggy for polynomials but not expressions - center_expr_check = True - if not radius_func_check: - raise TypeError("center was passed a function but radius was not a function") - if is_Expression(radius): - if len(radius.variables()) != 1: - raise ValueError("an expression with %s " %(len(radius.variables())) + \ - "variables cannot define the radii approximating a type IV point") - else: - radius_expr_check = True - else: - if is_Expression(center): - center_expr_check = True - if is_Expression(radius): - radius_expr_check = True - self._type = 4 - self._prec = prec - center_lst = [] - radius_lst = [] - self._center_func = center - self._radius_func = radius - if center_expr_check: - x = self._center_func.variables()[0] - if radius_expr_check: - y = self._radius_func.variables()[0] - for i in range(1,self._prec+1): - if center_expr_check: - #we use .subs for expressions to avoid deprecation - center_lst.append(self._center_func.subs({x:i})) - else: - #.subs for polynomials is currently buggy - center_lst.append(self._center_func(i)) - if radius_expr_check: - radius_lst.append(self._radius_func.subs({y:i})) - else: - radius_lst.append(self._radius_func(i)) - self._center_lst = center_lst - self._radius_lst = radius_lst - self._radius = None - self._center = None - if not error_check: - return - - if self._type == 4 and error_check: - if space_type == "projective": - for i in range(len(self._center_lst)): - center = self._center_lst[i] - radius = self._radius_lst[i] - #make sure the center is a point of projective space and not the point at infinity - if not isinstance(center, SchemeMorphism_point_projective_field): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise TypeError('could not convert %s to %s' %(center, self._base_space)) - if self._base_type == 'padic field': - if not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError("could not convert %s to %s" %(center, self._base_space)) - else: - # center is padic, not but an element of a scheme over a padic field. - # we convert to scheme over a padic field - center = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1)(center) - if center.scheme().base_ring().prime() != self._p: - raise ValueError("center must be an element of " + \ - "%s not %s" %self._base_space, center.scheme()) - else: - if center not in self._base_space: - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError('could not convert %s to %s' %(center, self._base_space)) - if center.scheme().ambient_space() != center.scheme(): - raise ValueError("the center of a point of Berkovich space over " + \ - "P^1(Cp(%s)) must be a point of Cp not %s" %(self._p,center.scheme())) - if center == (center.scheme())((1,0)): - raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ - "space cannot be centered at %s" %((center.scheme())((1,0)))) - #since we are over a field, we can normalize coordinates. all code assumes normalized coordinates - center.normalize_coordinates() - #make sure the radius coerces into the reals - if not is_RealNumber(radius): - if is_Expression(radius): - radius = RR(radius) - elif RR.has_coerce_map_from(radius.parent()): - radius = RR(radius) - else: - raise TypeError("the radius of a disk approximating a type IV point" + \ - "must coerce into the real numbers, %s does not coerce" %(radius)) - if i != 0: - #check containment for the sequence of disks - previous_center = self._center_lst[i-1] - previous_radius = self._radius_lst[i-1] - dist = self._custom_abs(center[0] - previous_center[0]) - if previous_radius < radius or dist > previous_radius: - raise ValueError("sequence of disks does not define a type IV point as " + \ - "containment is not proper") - self._center_lst[i] = center - self._radius_lst[i] = radius - return - elif space_type == "affine": - for i in range(len(self._center_lst)): - center = self._center_lst[i] - radius = self._radius_lst[i] - if self._base_type == 'padic field': - #make sure the center is in Cp - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise TypeError("could not convert %s to %s" %(center, self._base_space)) - elif not is_pAdicField(center.parent()): - #center is padic, not but an element of a padic field. we convert to padic field - center = (center.parent().fraction_field())(center) - if (center.parent()).prime() != self._p: - raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) - else: - #make sure the center is in the appropriate number field - if not(center in self._base_space): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError('could not convert %s to %s' %(center, self._base_space)) - #make sure the radius coerces into the reals - if not is_RealNumber(radius): - if is_Expression(radius): - radius = RR(radius) - elif RR.has_coerce_map_from(radius.parent()): - radius = RR(radius) - self._radius_lst[i] = radius - else: - raise ValueError("the radius of a disk approximating a type IV point must " + \ - "coerce into the real numbers, %s does not coerce" %(radius)) - if i != 0: - #check containment for the sequence of disks - previous_center = self._center_lst[i-1] - previous_radius = self._radius_lst[i-1] - dist = self._custom_abs(center - previous_center) - if previous_radius < radius or dist > previous_radius: - raise ValueError("sequence of disks does not define a type IV point as " + \ - "containment is not proper") - self._center_lst[i] = center - self._radius_lst[i] = radius - return - else: - raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ - "Berkovich_Element_Cp directly" ) - return - - #the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type - if error_check: - if space_type == 'affine': - if self._base_type == 'padic field': - #make sure the center is in Cp - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise TypeError("could not convert %s to %s" %(center, self._base_space)) - elif not is_pAdicField(center.parent()): - #center is padic, not but an element of a padic field. we convert to padic field - center = (center.parent().fraction_field())(center) - if (center.parent()).prime() != self._p: - raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) - else: - #make sure the center is in the appropriate number field - if not(center in self._base_space): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError('could not convert %s to %s' %(center, self._base_space)) - elif space_type == "projective": - if not isinstance(center, SchemeMorphism_point_projective_field): - try: - center = (self._base_space)(center) - except (ValueError, TypeError) as e: - raise TypeError("could not convert %s to %s" %(center, self._base_space)) - if self._base_type == 'padic field': - if not is_pAdicField(center.scheme().base_ring()): - if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError("could not convert %s to %s" %(center, self._base_space)) - else: - # center is padic, not but an element of a scheme over a padic field. - # we convert to scheme over a padic field - field_scheme = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1) - try: - center = field_scheme(center) - except (TypeError, ValueError) as e: - raise ValueError('could not convert %s to %s' %center, field_scheme) - if center.scheme().base_ring().prime() != self._p: - raise ValueError("center must be an element of " + \ - "%s not %s" %self._base_space, center.scheme()) - else: - if center not in self._base_space: - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError('could not convert %s to %s' %(center, self._base_space)) - if not(center.scheme().ambient_space() is center.scheme()): - raise ValueError("the center of a point of projective Berkovich space cannot be " + \ - "a point of %s" %(center.scheme())) - #since we are over a field, we normalize coordinates - center.normalize_coordinates() - else: - raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ - "Berkovich_Element_Cp directly") - - self._center = center - - #since this point is not type IV, these are None - self._center_func = None - self._center_lst = None - self._radius_lst = None - self._radius_func = None - - if (radius == None and power == None) or radius == 0: - self._type = 1 - self._radius = 0 - self._power = None - return - #In order to simplify our representation, type II and III points cannot be centered at infinity - if space_type == "projective": - #TODO use involution map to allow for infinity to be passed in as center - if center[1] == 0: - raise ValueError('type II and III points can not be centered at infinity') - if power != None: - if error_check: - if not(power.parent() is QQ): - try: - power = QQ(power) - except TypeError: - raise TypeError("power must convert to rationals") - if radius != None: - if radius != RR(self._p**power): - raise ValueError("conflicting inputs for power and radius") - self._power = power - self._radius = RR(self._p**power) - self._type = 2 - return - if radius != None: - if is_Expression(radius): - try: - power = QQ((radius.log(self._p)).expand_log()) - except TypeError: - pass - try: - radius = RR(radius) - self._radius = radius - except TypeError: - if len(radius.variables()) == 1: - raise ValueError('radius univariate function but center is constant. ' + \ - 'this does not define a type IV point') - raise TypeError("symbolic radius must be a real number") - if (not is_RealNumber(radius)) and power == None: - if RR.has_coerce_map_from(radius.parent()): - self._radius = RR(radius) - else: - raise TypeError("radius must coerce into real numbers") - else: - self._radius = radius - if power != None: - self._power = power - self._type = 2 - return - power = RR(radius.log(self._p)) - if power.is_integer(): - self._power = QQ(power) - self._type = 2 - else: - self._type = 3 - self._power = power - return - - raise TypeError('unknown error constructing point of Berkovich space over Cp') - - def _custom_abs(self, x): - """ - Returns the absolute value of ``x`` with respect to the norm on ``Cp``. - - Used to simplify code, as ``x`` may be a point of a number field - or a padic field. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(QQ, 3) - sage: Q1 = B(9) - sage: Q1._custom_abs(Q1.center()) - 1/9 - - :: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(9) - sage: Q1._custom_abs(Q1.center()) - 1/9 - """ - if self._base_type == 'padic field': - return x.abs() - if x.valuation(self._ideal) == Infinity: - return 0 - if isinstance(self._ideal, NumberFieldFractionalIdeal): - return (self.prime())**(-1*x.valuation(self._ideal)/self._ideal.absolute_ramification_index()) - return (self.prime())**(-1*x.valuation(self._ideal)) - - def center_function(self): - """ - Returns the function defining the centers of disks in the approximation. - - Not defined unless this point is a type IV point created by using - a univariate function to compute centers. - - OUTPUT: A univariate function. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(5) - sage: L. = PolynomialRing(Qp(5)) - sage: T = FractionField(L) - sage: f = T(1/t) - sage: R. = RR[] - sage: Y = FractionField(R) - sage: g = (40*pi)/x - sage: Q1 = B(f, g) - sage: Q1.center_function() - (1 + O(5^20))/((1 + O(5^20))*t) - """ - if self.type_of_point() != 4: - raise ValueError('center_function not defined for points which are not type IV') - if self._center_func == None: - raise ValueError('this type IV point does not have a center function') - return self._center_func - - def radius_function(self): - """ - Returns the function defining the radii of disks in the approximation. - - Not defined unless this point is a type IV point created by using - a univariate function to compute radii. - - OUTPUT: A univariate function. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(5) - sage: L. = PolynomialRing(Qp(5)) - sage: T = FractionField(L) - sage: f = T(1/t) - sage: R. = RR[] - sage: Y = FractionField(R) - sage: g = (40*pi)/x - sage: Q1 = B(f, g) - sage: Q1.radius_function() - 40.0000000000000*pi/x - """ - if self.type_of_point() != 4: - raise ValueError('center_function not defined for points which are not type IV') - if self._radius_func == None: - raise ValueError('this type IV point does not have a radius function') - return self._radius_func - - def precision(self): - """ - Returns the precision of a type IV point. - - Not defined for type I, II, or III points. - - OUTPUT: An integer. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) - sage: d.precision() - 3 - - TESTS:: - - sage: d.precision == d.prec - True - """ - if self._type in [1,2,3]: - raise AttributeError("type I, II, and III points do not have a precision") - return self._prec - - prec = precision - - def power(self): - r""" - Power of ``p`` such that `p^\text{power} = \text{radius}`. - - For type II points, always in `\QQ`. For type III points, - a real number. Not defined for type I or IV points. - - OUTPUT: - - - A rational for type II points. - - A real number for type III points. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(1, 9) - sage: Q1.power() - 2 - - :: - - sage: Q2 = B(1, 4) - sage: Q2.power() - 1.26185950714291 - """ - if self._type in [1,4]: - raise AttributeError("type I and IV points do not have a power") - return self._power - - def radius(self): - """ - Radius of the corresponding disk (or sequence of disks) in ``Cp``. - - OUTPUT: - - - A non-negative real number for points Types I-III. - - A list of non-negative real numbers for type IV points. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(1, 2/5) - sage: Q1.radius() - 0.400000000000000 - - :: - - sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) - sage: d.radius() - [1.76100000000000, 1.12300000000000, 1.11200000000000] - """ - if self._type == 4: - return self._radius_lst - return self._radius - - def diameter(self): - """ - Diameter function on Berkovich space. - - For type I, II, and III points, returns the radius. - - For type IV points returns either the last radius - in the finite approximation, or if a generating function - was given for the radii, the diameter is computed - as the limit of the function as it's variable tends - to infinity. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(3) - sage: Q1.diameter() - 0 - - :: - - sage: Q2 = B(1/2, 9) - sage: Q2.diameter() - 9.00000000000000 - - The diameter of a type IV point is the limit of the radii:: - - sage: R. = PolynomialRing(Qp(3)) - sage: f = R(2) - sage: S. = PolynomialRing(RR) - sage: S = FractionField(S) - sage: g = (y+1)/y - sage: B(f,g).diameter() - 1.0 - """ - if self._type == 4: - if self._radius_func == None: - return self._radius_lst[-1] - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(QQ, names="x") - x = R.gens()[0] - if is_Expression(self._radius_func): - radius_func_variable = self._radius_func.variables()[0] - radius_expr = self._radius_func.subs({radius_func_variable:x}) - else: - radius_expr = self._radius_func(x) - from sage.symbolic.ring import SymbolicRing as SR - radius_expr = SR(RR)(radius_expr) - return radius_expr.limit(x="oo") - return self._radius - - def path_distance_metric(self, other): - r""" - Returns the path distance metric distance between this point and ``other``. - - Also referred to as the hyperbolic metric, or the big metric. - - On the set of type II, III and IV points, the path distance metric - is a metric. Following Baker and Rumely, we extend - the path distance metric to type I points `x`, `y` by `\rho(x,x) = 0` and `\rho(x,y) = - \infty`. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(1/4, 4) - sage: Q2 = B(1/4, 6) - sage: Q1.path_distance_metric(Q2) - 0.369070246428542 - - :: - - sage: Q3 = B(1) - sage: Q3.path_distance_metric(Q1) - +infinity - - :: - - sage: Q3.path_distance_metric(Q3) - 0 - """ - if not isinstance(other, type(self)): - raise TypeError('other must be a point of Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError("other must be a point of the same Berkovich space") - if self.type_of_point() == 1 or other.type_of_point() == 1: - if self == other: - return 0 - else: - return RR(Infinity) - return 2*(self.join(other).diameter().log(self.prime()))\ - - self.diameter().log(self.prime())\ - - other.diameter().log(other.prime()) - - big_metric = path_distance_metric - - hyperbolic_metric = path_distance_metric - - def Hsia_kernel(self, other, basepoint): - """ - The Hsia kernel of this point and ``other``, - with basepoint ``basepoint``. - - The Hsia kernel with arbitrary basepoint - is a generalization of the Hsia kernel at infinity. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- A point of the same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 9) - sage: Q2 = B(1/27, 1/27) - sage: Q3 = B(1, 1/3) - sage: Q1.Hsia_kernel(Q2, Q3) - 0.111111111111111 - - :: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 9) - sage: Q2 = B(1/2) - sage: Q3 = B(1/2) - sage: Q1.Hsia_kernel(Q2, Q3) - +infinity - - """ - if not isinstance(other, type(self)): - raise TypeError('other must be a point of Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError("other must be a point of the same Berkovich space") - if not isinstance(basepoint, type(self)): - raise TypeError('basepoint must be a point of Berkovich space. basepoint was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich space") - if basepoint.type_of_point() == 1: - if self == basepoint or other == basepoint: - return RR(Infinity) - return (self.spherical_kernel(other))/ \ - (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) - - def small_metric(self, other): - r""" - Returns the small metric distance between this point and ``other``. - - The small metric is an extension of twice - the spherical distance on `P^1(\CC_p)`. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(1/4, 4) - sage: Q2 = B(1/4, 6) - sage: Q1.small_metric(Q2) - 0.0833333333333333 - - :: - - sage: B = Berkovich_Cp_Projective(QQ, 5) - sage: Q1 = B(0, 1) - sage: Q2 = B(99) - sage: Q1.small_metric(Q2) - 1.00000000000000 - - :: - - sage: Q3 = B(1/4, 4) - sage: Q3.small_metric(Q2) - 1.75000000000000 - - :: - - sage: Q2.small_metric(Q3) - 1.75000000000000 - """ - if not isinstance(other, Berkovich_Element_Cp): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other must be a point of the same Berkovich space') - gauss = self.parent()(RR(0), RR(1)) - g_greater_than_s = gauss.partial_order(self) - g_greater_than_o = gauss.partial_order(other) - if g_greater_than_s and g_greater_than_o: - return 2*(self.join(other,gauss).diameter()) - self.diameter() - other.diameter() - if not g_greater_than_s: - new_self = self.involution_map() - else: - new_self = self - if not g_greater_than_o: - new_other = other.involution_map() - else: - new_other = other - return 2*(new_self.join(new_other,gauss).diameter()) \ - - new_self.diameter() - new_other.diameter() - - def Hsia_kernel_infinity(self, other): - r""" - Return the Hsia kernel at infinity of this point with ``other``. - - The Hsia kernel at infinity is the natural extension of the - absolute value on `\CC_p` to Berkovich space. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(1/4, 4) - sage: Q2 = B(1/4, 6) - sage: Q1.Hsia_kernel_infinity(Q2) - 6.00000000000000 - - :: - - sage: R. = QQ[] - sage: A. = NumberField(x^3+20) - sage: ideal = A.ideal(-1/2*a^2 + a - 3) - sage: B = Berkovich_Cp_Projective(A, ideal) - sage: Q1 = B(4) - sage: Q2 = B(0, 1.5) - sage: Q1.Hsia_kernel_infinity(Q2) - 1.50000000000000 - """ - return self.join(other).diameter() - - def center(self): - r""" - Returns the center of the corresponding disk (or sequence of disks) - in `\CC_p`. - - OUTPUT: An element of the ``base`` of the parent Berkovich space. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B(3, 1).center() - 3 + O(3^21) - - :: - - sage: C = Berkovich_Cp_Projective(3) - sage: C(3, 1).center() - (3 + O(3^21) : 1 + O(3^20)) - - :: - - sage: R. = QQ[] - sage: A. = NumberField(x^3 + 20) - sage: ideal = A.ideal(-1/2*a^2 + a - 3) - sage: B = Berkovich_Cp_Projective(A, ideal) - sage: B(a^2+4).center() - (a^2 + 4 : 1) - """ - if self._type == 4: - return self._center_lst - return self._center - - def type_of_point(self): - """ - Returns the type of this point of Berkovich space over ``Cp`` - - OUTPUT: An integer between 1 and 4 inclusive. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B(1).type_of_point() - 1 - - :: - - sage: B(0, 1).type_of_point() - 2 - """ - return ZZ(self._type) - - def prime(self): - """ - The residue characteristic of the parent. - - OUTPUT: A prime integer. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B(1).prime() - 3 - """ - return ZZ(self._p) - - def __ne__(self, other): - """ - Non-equality operator. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(3, 3**(1/2)) - sage: Q2 = B(3, RR(3**(1/2))) - sage: Q1 != Q2 - False - """ - return not (self == other) - - def _repr_(self): - """ - String representation of this point. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective((3)) - sage: B(2, 1) - Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 - """ - if self._type == 1: - return "Type I point centered at " + format(self._center) - elif self._type == 2: - return "Type II point centered at " \ - + format(self._center) \ - + " of radius %s^%s" %(self._p, self._power) - elif self._type == 3: - return "Type III point centered at " \ - + format(self._center) + " of radius " \ - + format(self._radius) - else: - if self._center_func != None and self._radius_func != None: - return "Type IV point of precision %s " %self._prec + \ - "with centers given by %s and radii given by %s"\ - %(self._center_func, self._radius_func) - else: - return "Type IV point of precision %s, approximated " %self._prec + \ - "by disks centered at %s ... with radii %s ..." \ - %(self._center_lst[:min(self._prec, 2)], self._radius_lst[:min(self._prec, 2)]) - - def _latex_(self): - r""" - LaTeX representation of this point. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective((3)) - sage: latex(B(2, 1)) - \text{type 2 Point of } \text{Projective Berkovich line over } - \Bold{C}_{3} \text{equivalent to the disk centered at - (2 + O(3^20) : 1 + O(3^20)) of radius 1.00000000000000 in } \Bold{C}_3 - """ - from sage.misc.latex import latex - if self._type == 1: - text = r"the point %s of } \Bold{C}_%s" %(self._center, self._p) - elif self._type in [2,3]: - text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" \ - %(self._center, self._radius, self._p) - else: - text = "the sequence of disks with centers %s } " %self._center_lst[:2] + \ - r"\ldots \text{ and radii %s } \ldots" %self._radius_lst[:2] - return r"\text{type %s Point of }" %(self._type) \ - + latex(self.parent()) + r"\text{equivalent to " + text - -class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): - r""" - Element class of the Berkovich affine line over `\CC_p`. - - Elements are categorized into four types, represented by specific data: - - - Type I points are represented by a center in the ``base`` of the parent Berkovich space, - which is `\QQ_p`, a finite extension of `\QQ_p`, or a number field. - - - Type II points are represented by a center in the ``base`` of the parent Berkovich space, - and a rational power of `p`. - - - Type III points are represented by a center in the ``base`` of the parent Berkovich space, - and a radius in `[0,\infty)`. - - - Type IV points are represented by a finite list of centers in the ``base`` of the parent - Berkovich space and a finite list of radii in `[0,\infty)`. Type IV points can be created - from univariate functions, allowing for arbitrary precision. - - INPUT: - - - ``center`` -- For type I, II, and III points, the center of the - corresponding disk in `\CC_p`. If the parent Berkovich space was created using a number field - `K`, then ``center`` must be an element of `K`. Otherwise, ``center`` must be an element of a - p-adic field. For type IV points, can be a list of centers used to approximate the point or a - univariate function that computes the centers (computation starts at 1). - - - ``radius`` -- (optional) For type I, II, and III points, the radius of the - corresponding disk in ``Cp``. Must coerce into the real numbers. For type IV points, - can be a list of radii used to approximate the point or a univariate function that - computes the radii (computation starts at 1). - - - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies - the power of ``p`` such that `p^\text{power}` = radius. - - - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point. - - - ``error_check`` -- (default: True) If error checking should be run on input. If - input is correctly formatted, can be set to ``False`` for better performance. - WARNING: with error check set to ``False``, any error in the input will lead to - incorrect results. - - EXAMPLES: - - Type I points can be created by specifying the corresponding point of ``Cp``:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: a = B(4) - sage: a - Type I point centered at 1 + 3 + O(3^20) - - The center of a point can be an element of a finite extension of ``Qp``:: - - sage: A. = Qq(27) - sage: a = B(1+t) - sage: a - Type I point centered at (t + 1) + O(3^20) - - Type II and III points can be created by specifying a center and a radius:: - - sage: b = B(2, 3**(1/2)); b - Type II point centered at 2 + O(3^20) of radius 3^1/2 - sage: c = B(2, 1.6); c - Type III point centered at 2 + O(3^20) of radius 1.60000000000000 - - Some type II points may be mistaken for type III points:: - - sage: b = B(3, 3**0.5); b #not tested - Type III point centered at 3 + O(3^21) of radius 1.73205080756888 - - To avoid these errors, specify the power instead of the radius:: - - sage: b = B(3, power=RR(1/100000)); b - Type II point centered at 3 + O(3^21) of radius 3^1/100000 - - Type IV points can be constructed in a number of ways, the first being - from a list of centers and radii used to approximate the point:: - - sage: d = B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]); d - Type IV point of precision 3, approximated by disks centered at - [2 + O(3^20), 2 + O(3^20)] ... with radii [1.76100000000000, 1.12300000000000] ... - - Type IV points can be constructed from univariate functions, with arbitrary precision:: - - sage: A. = Qq(27) - sage: R. = PolynomialRing(A) - sage: f = (1+t)^2*x - sage: S. = PolynomialRing(RR) - sage: S = FractionField(S) - sage: g = (y+1)/y - sage: d = B(f, g, prec=100); d - Type IV point of precision 100 with centers given by - ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y - - For increased performance, error_check can be set to ``False``. WARNING: with error check set - to ``False``, any error in the input will lead to incorrect results:: - - sage: d = B(f, g, prec=100,error_check=False); d - Type IV point of precision 100 with centers given by - ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y - - When creating a Berkovich space backed by a number field, points can be created similarily:: - - sage: R. = QQ[] - sage: A. = NumberField(x^3 + 20) - sage: ideal = A.prime_above(3) - sage: B = Berkovich_Cp_Projective(A, ideal) - sage: Q1 = B(a); Q1 - Type I point centered at (a : 1) - - :: - - sage: Q2 = B(a+1, 3); Q2 - Type II point centered at (a + 1 : 1) of radius 3^1 - - TESTS:: - - sage: A = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = A(3, 1); Q1 - Type II point centered at 3 + O(3^21) of radius 3^0 - - sage: Q2 = A(2.5, 1); Q2 - Type II point centered at 1 + 2*3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + - 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + - 3^18 + 3^19 + O(3^20) of radius 3^0 - - sage: Q5 = A(3, 0); Q5 - Type I point centered at 3 + O(3^21) - - sage: A(Zp(3)(2), 2).center().parent() == A(Qp(3)(2), 2).center().parent() - True - - sage: Q1 == Q2 - True - - sage: Q1 == Q5 - False - - sage: Q3 = A(Qp(3)(3), power=0, error_check=False); Q3 - Type II point centered at 3 + O(3^21) of radius 3^0 - - sage: Q4 = A(3, 3**0); Q4 - Type II point centered at 3 + O(3^21) of radius 3^0 - - sage: Q5 = A(3, power = 1/2); Q5 - Type II point centered at 3 + O(3^21) of radius 3^1/2 - - sage: Q6 = A(3, RR(3**(1/2))); Q6 - Type III point centered at 3 + O(3^21) of radius 1.73205080756888 - - sage: Q5 == Q6 - True - - sage: k = Qp(5) - sage: R. = k[] - sage: l. = k.extension(x^2 - 5) - sage: B = Berkovich_Cp_Affine(Qp(5)) - sage: B(w, power=1) - Type II point centered at w + O(w^41) of radius 5^1 - """ - def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): - #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space - #unless we are passed a point of projective Berkovich space - Element.__init__(self, parent) - self._p = parent.prime() - self._base_space = parent.base() - self._base_type = parent._base_type - self._ideal = parent._ideal - - #if this is a point of projective berkovich space, we raise an error - if isinstance(center, Berkovich_Element_Cp_Projective): - raise TypeError('use as_affine_point to convert to affine Berkovich space') - - Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, \ - prec=prec, space_type="affine", error_check=error_check) - - def as_projective_point(self): - r""" - Returns the corresponding point of projective Berkovich space. - - We identify affine Berkovich space with the subset `P^1_{\text{Berk}}(C_p) - (1 : 0)`. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(5) - sage: B(5).as_projective_point() - Type I point centered at (5 + O(5^21) : 1 + O(5^20)) - - :: - - sage: B(0, 1).as_projective_point() - Type II point centered at (0 : 1 + O(5^20)) of radius 5^0 - - :: - - sage: L. = PolynomialRing(Qp(5)) - sage: T = FractionField(L) - sage: f = T(1/t) - sage: R. = RR[] - sage: Y = FractionField(R) - sage: g = (40*pi)/x - sage: Q2 = B(f, g) - sage: Q2.as_projective_point() - Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) - and radii given by 40.0000000000000*pi/x - """ - new_space = Berkovich_Cp_Projective(self.parent().base_ring(), self.parent().ideal()) - if self.type_of_point() == 1: - return new_space(self.center()) - elif self.type_of_point() == 2: - return new_space(self.center(), power=self.power()) - elif self.type_of_point() == 3: - return new_space(self.center(), self.radius()) - if self._center_func == None: - center = self.center() - else: - center = self.center_function() - if self._radius_func == None: - radius = self.radius() - else: - radius = self.radius_function() - return new_space(center, radius, prec=self.prec()) - - def center(self): - """ - Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. - - OUTPUT: - - - For type I-III points, a point of ``Cp``. - - For type IV points, a list of points of ``Cp``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(2, 1) - sage: Q1.center() - 2 + O(3^20) - - :: - - sage: d = B([4, 2], [4, 2]) - sage: d.center() - [1 + 3 + O(3^20), 2 + O(3^20)] - """ - return super().center() - - def __eq__(self, other): - """ - Equality operator. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(1, RR(3**(1/2))) - sage: Q2 = B(1, 3**(1/2)) - sage: Q1 == Q2 - True - - :: - - sage: Q3 = B(1) - sage: Q4 = B(4) - sage: Q3 == Q4 - False - - :: - - sage: Q5 = B(1, 4) - sage: Q1 == Q5 - False - - :: - - sage: Q1 == Q3 - False - """ - if other is self: - return True - if not isinstance(other, Berkovich_Element_Cp_Affine): - return False - if other.parent() != self.parent(): - return False - stype = self.type_of_point() - otype = other.type_of_point() - if stype == otype and stype == 1: - return self.center() == other.center() - elif stype == otype and stype == 4: - raise NotImplementedError("Equality for type IV points not yet implemented") - elif stype in [2,3] and otype in [2,3]: - if self.radius() != other.radius(): - return False - center_dist = self._custom_abs(self.center() - other.center()) - return center_dist <= self.radius() - else: - return False - - def __hash__(self): - """ - Returns the hash of this point. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(1, RR(3**(1/2))) - sage: Q2 = B(1, 3**(1/2)) - sage: hash(Q1) == hash(Q2) - True - - :: - - sage: R. = QQ[] - sage: A. = NumberField(x^3+20) - sage: ideal = A.ideal(-1/2*a^2 + a - 3) - sage: B = Berkovich_Cp_Projective(A, ideal) - sage: Q1 = B(a^2+1, 2) - sage: Q2 = B(0, 2) - sage: hash(Q1) == hash(Q2) - True - """ - if self.type_of_point() == 1: - return hash(str(self.center())) - elif self.type_of_point() == 4: - raise NotImplementedError('hash not defined for type IV points') - return hash(str(self.radius())) - - def partial_order(self,other): - r""" - The standard partial order on Berkovich space. - - Roughly, the partial order corresponds to containment of - the corresponding disks in `\CC_p`. - - For example, let x and y be points of type II or III. - If x has center `c_1` and radius `r_1` and y has center - `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` - is a subset of `D(c_2,r_2)` in `\CC_p`. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: - - - ``True`` -- If self > other in the standard partial order. - - ``False`` -- If self < other in the standard partial order. - - ``None`` -- If the two points are not comparable. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2, 4) - sage: Q2 = B(2, 6) - sage: Q1.partial_order(Q2) - False - - :: - - sage: Q3 = B(1/2) - sage: Q1.partial_order(Q3) - True - - :: - - sage: Q4 = B(1/81, 1) - sage: Q4.partial_order(Q1) is None - True - - :: - - sage: Q4.partial_order(Q3) is None - True - """ - #error check, then convert to projective berkovich space to do the partial order - if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other must be a point of the same affine Berkovich space') - return self.as_projective_point().partial_order(other.as_projective_point()) - - def join(self, other, basepoint=Infinity): - """ - Computes the join of this point and ``other`` with respect to ``basepoint``. - - The join is first point that lies on the interesection - of the path from this point to ``basepoint`` and the path from ``other`` to - ``basepoint``. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point or Infinity. - - OUTPUT: A point of the same Berkovich space. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2, 1) - sage: Q2 = B(2, 2) - sage: Q1.join(Q2) - Type III point centered at 2 + O(3^20) of radius 2.00000000000000 - - :: - - sage: Q3 = B(5) - sage: Q3.join(Q1) - Type II point centered at 2 + 3 + O(3^20) of radius 3^0 - - :: - - sage: Q3.join(Q1, basepoint=Q2) - Type II point centered at 2 + O(3^20) of radius 3^0 - - TESTS:: - - sage: Q4 = B(1/3**8 + 2, 1) - sage: Q2.join(Q4, basepoint = Q1) - Type III point centered at 2 + O(3^20) of radius 2.00000000000000 - - :: - - sage: Q5 = B(2, 1/9) - sage: Q6 = B(1, 1/27) - sage: Q4.join(Q5, basepoint=Q6) - Type II point centered at 1 + O(3^20) of radius 3^0 - - :: - - sage: Q7 = B(1/27, 1/27) - sage: Q1.join(Q7, Q2) - Type III point centered at 2 + O(3^20) of radius 2.00000000000000 - """ - #we error check and then pass to projective space to do the join - if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other must be a point of the same affine Berkovich space') - if self.type_of_point() == 4 or other.type_of_point() == 4: - raise NotImplementedError("join with type IV points not implemented") - - proj_self = self.as_projective_point() - proj_other = other.as_projective_point() - - if basepoint == Infinity: - return proj_self.join(proj_other).as_affine_point() - - if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise TypeError('basepoint must a point of affine Berkovich space. basepoint was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same affine Berkovich space") - if basepoint.type_of_point() == 4: - raise NotImplementedError("join not implemented for type IV basepoint") - proj_basepoint = basepoint.as_projective_point() - return proj_self.join(proj_other, proj_basepoint).as_affine_point() - - - def involution_map(self): - r""" - Returns the image of this point under the involution map. - - The involution map is the extension of the map ``z |-> 1/z`` - on `\CC_p` to Berkovich space. - - For Affine Berkovich Space, not defined for the type I - point centered at 0. - - OUTPUT: A point of the same Berkovich space. - - EXAMPLES: - - The involution map is 1/z on type I points:: - - sage: B = Berkovich_Cp_Affine((3)) - sage: Q1 = B(1/2) - sage: Q1.involution_map() - Type I point centered at 2 + O(3^20) - - :: - - sage: Q2 = B(0, 1/3) - sage: Q2.involution_map() - Type II point centered at 0 of radius 3^1 - - :: - - sage: Q3 = B(1/3, 1/3) - sage: Q3.involution_map() - Type II point centered at 3 + O(3^21) of radius 3^-3 - - """ - if self.type_of_point() == 1: - if self.center() == 0: - raise ValueError("involution map not deffined on affine type I point centered at 0") - return self.parent()(1/self.center()) - - zero = self.parent()(QQ(0)) - radius = self.radius() - - if self.type_of_point() in [2,3]: - zero_contained_in_self = self.partial_order(zero) - if zero_contained_in_self: - if self.type_of_point() == 2: - power = self.power() - return self.parent()(0, power=-power) - return self.parent()(0, RR(1/radius)) - return self.parent()(1/self.center(), RR( radius / (self._custom_abs(self.center())**2) ) ) - - new_center_lst = [] - new_radius_lst = [] - for i in range(len(center)): - berk_point = self.parent()(self.center()[i], radius[i]) - zero_check = berk_point.partial_order(zero) - if zero_check: - new_center = 0 - new_radius = RR(1/radius[i]) - else: - new_center = 1/self.center()[i] - new_radius = RR(radius[i] / (self._custom_abs(self.center()[i])**2)) - new_center_lst.append(new_center) - new_radius_lst.append(new_radius) - return self.parent()(new_center_lst, new_radius_lst, error_check=False) - - def contained_in_interval(self, start, end): - """ - Checks if this point is an element of the interval [``start``, ``end``]. - - INPUT: - - - ``start`` -- A point of the same Berkovich space as this point. - - ``end`` -- A point of the same Berkovich space as this point. - - OUTPUT: - - - ``True`` if this point is an element of [``start``, ``end``]. - - ``False`` otherwise. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective((3)) - sage: Q1 = B(2, 1) - sage: Q2 = B(2, 4) - sage: Q3 = B(1/3) - sage: Q2.contained_in_interval(Q1, Q3.join(Q1)) - False - - :: - - sage: Q4 = B(1/81, 1) - sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) - True - """ - if not isinstance(start, Berkovich_Element_Cp_Affine): - raise TypeError("start must be a point of affine Berkovich space. start was %s" %start) - if start.parent() != self.parent(): - raise ValueError("start must be a point of the same Berkovich space as this point") - if not isinstance(end, Berkovich_Element_Cp_Affine): - raise TypeError("end must be a point of affine Berkovich space. end was %s" %end) - if end.parent() != self.parent(): - raise ValueError("end must be a point of the same Berkovich space as this point") - - proj_self = self.as_projective_point() - proj_start = start.as_projective_point() - proj_end = end.as_projective_point() - return proj_self.contained_in_interval(proj_start, proj_end) - - def potential_kernel(self, other, basepoint): - """ - The potential kernel of this point with ``other`` with basepoint ``basepoint``. - - The potential kernel is the hyperbolic distance between ``basepoint`` and the join - of this point with ``other`` relative to ``basepoint``. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- A point of the same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(27, 1) - sage: Q2 = B(1/3, 2) - sage: Q3 = B(1/9, 1/2) - sage: Q3.potential_kernel(Q1, Q2) - 0.369070246428543 - """ - if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other must be a point of the same affine Berkovich space') - if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise TypeError('basepoint must be a point of affine Berkovich space. basepoint was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError('basepoint must be a point of the same affine Berkovich space') - return basepoint.path_distance_metric(self.join(other, basepoint)) - - def spherical_kernel(self,other): - r""" - The spherical kernel of this point with ``other``. - - The spherical kernel is one possible extension of - the spherical distance on `A^1(\CC_p)` to the Berkovich - Affine line. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2, 9) - sage: Q2 = B(1/27, 1/27) - sage: Q1.spherical_kernel(Q2) - 0.111111111111111 - - """ - if not isinstance(other,Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of an affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other was not a point of the same affine Berkovich space') - gauss_point = self.parent()(RR(0), RR(1)) - w = self.join(other,gauss_point) - dist = gauss_point.path_distance_metric(w) - if dist == Infinity: - return 0 - return (self.prime())**(-1*dist) - - def diameter(self, basepoint=Infinity): - r""" - Generalized diameter function. - - If the basepoint is infinity, the diameter is equal to - the limit of the radii of the corresponding disks in `\CC_p`. - - If the basepoint is not infinity, the diameter - is the Hsia kernel of this point with itself at - basepoint ``basepoint``. - - INPUT: - - - ``basepoint`` -- (default = Infinity) A point of the - same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(1/81, 1) - sage: Q2 = B(1/3) - sage: Q1.diameter(Q2) - 0.00137174211248285 - - :: - - sage: Q2.diameter(Q2) - +infinity - """ - if basepoint == Infinity: - return super().diameter() - return self.Hsia_kernel(self, basepoint) - -class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): - r""" - Element class of the Berkovich projective line over `\CC_p`. - - Elements are categorized into four types, represented by specific data: - - - Type I points are represented by a center in the ``base`` of the parent Berkovich space, - which is projective space of dimension 1 over either `\QQ_p`, a finite extension of `\QQ_p`, - or a number field. - - - Type II points are represented by a center in the ``base`` of the parent Berkovich space, - and a rational power of `p`. - - - Type III points are represented by a center in the ``base`` of the parent Berkovich space, - and by a radius, a real number, in `[0,\infty)`. - - - Type IV points are represented by a finite list of centers in the ``base`` of the parent - Berkovich space and a finite list of radii in `[0,\infty)`. - - The projective Berkovich line is viewed as the one-point compactification of - the affine Berkovich line. The projective Berkovich line therefore contains - every point of the affine Berkovich line, along with a type I point centered - at infinity. - - INPUT: - - - ``center`` -- For type I, II, and III points, the center of the - corresponding disk in `P^1(\CC_p)`. If the parent Berkovich space was created using a number field - `K`, then ``center`` can be an element of `P^1(K)`. Otherwise, ``center`` - must be an element of a projective space of dimension 1 over a padic field. - For type IV points, can be a list of centers used to approximate the point or a - univariate function that computes the centers (computation starts at 1). - - - ``radius`` -- (optional) For type I, II, and III points, the radius of the - corresponding disk in `\CC_p`. Must coerce into the real numbers. For type IV points, - can be a list of radii used to approximate the point or a univariate function that - computes the radii (computation starts at 1). - - - ``power`` -- (optional) Rational number. Used for constructing type II points; specifies - the power of ``p`` such that `p^\text{power}` = radius - - - ``prec`` -- (default: 20) The number of disks to be used to approximate a type IV point - - - ``error_check`` -- (default: True) If error checking should be run on input. If - input is correctly formatted, can be set to ``False`` for better performance. - WARNING: with error check set to ``False``, any error in the input will lead to - incorrect results. - - EXAMPLES: - - Type I points can be created by specifying the corresponding point of `P^1(\CC_p)`:: - - sage: S = ProjectiveSpace(Qp(5), 1) - sage: P = Berkovich_Cp_Projective(S); P - Projective Berkovich line over Cp(5) of precision 20 - - sage: a = S(0,1) - sage: Q1 = P(a); Q1 - Type I point centered at (0 : 1 + O(5^20)) - - sage: Q2 = P((1,0)); Q2 - Type I point centered at (1 + O(5^20) : 0) - - Type II and III points can be created by specifying a center and a radius:: - - sage: Q3 = P((0,5), 5**(3/2)); Q3 - Type II point centered at (0 : 1 + O(5^20)) of radius 5^3/2 - - sage: Q4 = P(0, 3**(3/2)); Q4 - Type III point centered at (0 : 1 + O(5^20)) of radius 5.19615242270663 - - Type IV points can be created from lists of centers and radii:: - - sage: b = S((3,2)) #create centers - sage: c = S((4,3)) - sage: d = S((2,3)) - sage: L = [b, c, d] - sage: R = [1.761, 1.123, 1.112] - sage: Q5 = P(L, R); Q5 - Type IV point of precision 3, approximated by disks centered at - [(4 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + - 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20) : - 1 + O(5^20)), (3 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + - 5^10 + 3*5^11 + 5^12 + 3*5^13 + 5^14 + 3*5^15 + 5^16 + 3*5^17 + 5^18 + 3*5^19 + O(5^20) : - 1 + O(5^20))] ... with radii [1.76100000000000, 1.12300000000000] ... - - Type IV points can also be created from univariate functions. Since the centers of - the sequence of disks can not be the point at infinity in `P^1(\CC_p)`, only functions - into `\CC_p` are supported:: - - sage: L. = PolynomialRing(Qp(5)) - sage: T = FractionField(L) - sage: f = T(1/t) - sage: R. = RR[] - sage: Y = FractionField(R) - sage: g = (40*pi)/x - sage: Q6 = P(f, g); Q6 - Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) - and radii given by 40.0000000000000*pi/x - - TESTS:: - - sage: P((1,0), 3) - Traceback (most recent call last): - ... - ValueError: type II and III points can not be centered at infinity - - """ - def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): - #if we are given a point of Affine Berkovich Space, we do the conversion - #otherwise we call the Berkovich_Element_Cp constructor with space_type="projective" - - Element.__init__(self, parent) - self._p = parent.prime() - self._base_space = parent.base() - self._base_type = parent._base_type - self._ideal = parent._ideal - - #conversion from Affine points is handled in this constructor - if isinstance(center, Berkovich_Element_Cp_Affine): - raise TypeError('use as_projective_point to convert to projective Berkovich space') - - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ - prec=prec,space_type="projective",error_check=error_check) - - def as_affine_point(self): - """ - Returns the corresponding affine point after dehomogenizing at infinity. - - OUTPUT: A point of affine Berkovich space. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(5) - sage: B(5).as_affine_point() - Type I point centered at 5 + O(5^21) - - :: - - sage: B(0, 1).as_affine_point() - Type II point centered at 0 of radius 5^0 - - :: - - sage: L. = PolynomialRing(Qp(5)) - sage: T = FractionField(L) - sage: f = T(1/t) - sage: R. = RR[] - sage: Y = FractionField(R) - sage: g = (40*pi)/x - sage: Q2 = B(f, g) - sage: Q2.as_affine_point() - Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) - and radii given by 40.0000000000000*pi/x - """ - if self.center()[1] == 0: - raise ValueError('cannot convert infinity to affine Berkovich space') - new_space = Berkovich_Cp_Affine(self.parent().base_ring(), self.parent().ideal()) - if self.type_of_point() in [1,2,3]: - center = self.center()[0] - if self.type_of_point() == 1: - return new_space(center) - elif self.type_of_point() == 2: - return new_space(center, power=self.power()) - elif self.type_of_point() == 3: - return new_space(center, self.radius()) - if self._center_func == None: - center = [i[0] for i in self.center()] - else: - center = self.center_function() - if self._radius_func == None: - radius = self.radius() - else: - radius = self.radius_function() - return new_space(center, radius, prec=self.prec()) - - def center(self): - r""" - Returns the center of the corresponding disk (or sequence of disks) in `P^1(\CC_p)`. - - OUTPUT: - - - For type I-III points, a point of `P^1(\CC_p)`. - - For type IV points, a list of points of `P^1(\CC_p)`. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 1) - sage: Q1.center() - (2 + O(3^20) : 1 + O(3^20)) - - :: - - sage: d = B([4, 2], [4, 2]) - sage: d.center() - [(1 + 3 + O(3^20) : 1 + O(3^20)), (2 + O(3^20) : 1 + O(3^20))] - """ - return super().center() - - def __eq__(self, other): - """ - Equality operator. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B([2, 2], RR(3**(1/2))) - sage: Q2 = B([1, 1], 3**(1/2)) - sage: Q1 == Q2 - True - - :: - - sage: Q3 = B(1) - sage: Q4 = B(4) - sage: Q3 == Q4 - False - - :: - - sage: Q5 = B(1, 4) - sage: Q1 == Q5 - False - - :: - - sage: Q1 == Q3 - False - """ - if other is self: - return True - if not isinstance(other, Berkovich_Element_Cp_Projective): - return False - if other.parent() != self.parent(): - return False - stype = self.type_of_point() - otype = other.type_of_point() - if stype == otype and stype == 1: - return self.center() == other.center() - elif stype == otype and stype == 4: - raise NotImplementedError("equality for type IV points not implemented") - elif stype in [2,3] and otype in [2,3]: - if self.radius() != other.radius(): - return False - scent = self.center()[0] - ocent = other.center()[0] - center_dist = self._custom_abs(scent - ocent) - return center_dist <= self.radius() - else: - return False - - def __hash__(self): - """ - Returns the hash of this point. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: P = ProjectiveSpace(B.base_ring(), 1) - sage: Q1 = B(P.point([2,2], False), RR(3**(1/2))) - sage: Q2 = B([1,1], 3**(1/2)) - sage: hash(Q1) == hash(Q2) - True - - :: - - sage: R. = QQ[] - sage: A. = NumberField(x^3+20) - sage: ideal = A.ideal(-1/2*a^2 + a - 3) - sage: B = Berkovich_Cp_Projective(A, ideal) - sage: Q1 = B(a^2+1, 2) - sage: Q2 = B(0, 2) - sage: hash(Q1) == hash(Q2) - True - """ - if self.type_of_point() == 1: - return hash(str(self.center())) - elif self.type_of_point() == 4: - raise ValueError('hash not defined for type IV points') - return hash(str(self.radius())) - - def partial_order(self,other): - r""" - The standard partial order on Berkovich space. - - Roughly, the partial order corresponds to containment of - the corresponding disks in ``Cp``. - - For example, let x and y be points of type II or III. - If x has center `c_1` and radius `r_1` and y has center - `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` - is a subset of `D(c_2,r_2)` in `\CC_p`. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: - - - ``True`` - If self => other in the standard partial order. - - ``False`` - If other > self in the standard partial order. - - ``None`` - If the two points are not comparable. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) - sage: Q1 = B(2, 4) - sage: Q2 = B(2, 6) - sage: Q1.partial_order(Q2) - False - - :: - - sage: Q3 = B(1/2) - sage: Q1.partial_order(Q3) - True - - :: - - sage: Q4 = B(1/81, 1) - sage: Q4.partial_order(Q1) == None - True - - We check infinity works in the partial order:: - - sage: Q5 = B((1,0)) - sage: Q6 = B(3, 3) - sage: Q6.partial_order(Q5) - True - - TESTS:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B((1,0)) - sage: Q2 = B(0) - sage: Q1.partial_order(Q2) == None - True - - :: - - sage: Q2.partial_order(Q2) - True - - :: - - sage: Q3 = B(2) - sage: Q2.partial_order(Q3) == None - True - - :: - - sage: Q4 = B(1/27, 1) - sage: Q4.partial_order(Q2) == None - True - - :: - - sage: Q2.partial_order(Q4) == None - True - - :: - - sage: Q5 = B(0,1) - sage: Q2.partial_order(Q5) - False - - :: - - sage: Q6 = B([3,2],[4,1/27]) - sage: Q4.partial_order(Q6) == None - True - - :: - - sage: Q5.partial_order(Q6) - True - - :: - - sage: Q7 = B(0,1/27) - sage: Q5.partial_order(Q7) - True - - :: - - sage: Q8 = B(0, 2) - sage: Q5.partial_order(Q8) - False - - :: - - sage: Q9 = B(1/9,10) - sage: Q5.partial_order(Q9) - False - """ - if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other must be a point of the same projective Berkovich space') - - #if self or other is infinity, we apply the involution map - infty = self.parent()((1,0)) - zero = self.parent()(0) - if self == infty or other == infty: - if self == zero or other == zero: - return None - newself = self.involution_map() - newother = other.involution_map() - return newself.partial_order(newother) - - if self.type_of_point() == 1: - if other.type_of_point() in [1,4]: - if self == other: - return True - return None - else: - s_less_than_o = other.partial_order(self) - if s_less_than_o == None: - return None - return not s_less_than_o - else: - if other.type_of_point() == 1: - dist = self._custom_abs(self.center()[0] - other.center()[0]) - if dist <= self.radius(): - return True - return None - elif other.type_of_point() == 4: - center = other.center()[-1] - dist = self._custom_abs(self.center()[0] - center[0]) - if dist <= self.radius() and other.radius()[-1] <= self.radius(): - return True - return None - else: - dist = self._custom_abs(self.center()[0]-other.center()[0]) - if dist <= self.radius(): - if other.radius() <= self.radius(): - return True - return False - else: - if dist <= other.radius(): - return False - return None - - def join(self, other, basepoint=Infinity): - """ - Computes the join of this point and ``other``, with respect to ``basepoint``. - - The join is first point that lies on the interesection - of the path from this point to ``basepoint`` and the path from ``other`` to - ``basepoint``. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- (default: Infinity) A point of the same - Berkovich space as this point, or infinity. - - OUTPUT: A point of the same Berkovich space. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) - sage: Q1 = B(2, 1) - sage: Q2 = B(2, 2) - sage: Q1.join(Q2) - Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 - - :: - - sage: Q3 = B(5) - sage: Q3.join(Q1) - Type II point centered at (2 + 3 + O(3^20) : 1 + O(3^20)) of radius 3^0 - - :: - - sage: Q3.join(Q1, basepoint=Q2) - Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 - - TESTS:: - - sage: Q4 = B(1/3**8+2, 1) - sage: Q2.join(Q4, basepoint=Q1) - Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 - - sage: Q5 = B(2, 1/9) - sage: Q6 = B(1, 1/27) - sage: Q4.join(Q5, basepoint=Q6) - Type II point centered at (1 + O(3^20) : 1 + O(3^20)) of radius 3^0 - - sage: Q7 = B(1/27, 1/27) - sage: Q1.join(Q7, Q2) - Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 - - sage: Q1.join(Q2, Q7) - Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 - - sage: Q8 = B(0, power=1/3) - sage: Q9 = B(0, power=1/2) - sage: Q8.join(Q9) - Type II point centered at (0 : 1 + O(3^20)) of radius 3^1/2 - - sage: R. = QQ[] - sage: A. = NumberField(x^3+20) - sage: ideal = A.prime_above(3) - sage: C = Berkovich_Cp_Projective(A, ideal) - sage: Q10 = C(a, 1/9) - sage: Q10.join(Q9) - Traceback (most recent call last): - ... - ValueError: other must be a point of the same projective Berkovich line - - sage: Q11 = C(0, 1/3) - sage: Q11.join(Q10) - Type II point centered at (0 : 1) of radius 3^0 - """ - if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) - if other.parent() != self.parent(): - raise ValueError('other must be a point of the same projective Berkovich line') - - #if either self or other is type IV, we use the last disk in the approximation - if self.type_of_point() == 4: - new_center = self.center()[-1] - new_radius = self.radius()[-1] - return self.parent()(new_center,new_radius).join(other) - if other.type_of_point() == 4: - new_center = other.center()[-1] - new_radius = other.radius()[-1] - return self.join(self.parent()(new_center,new_radius)) - - #we deal with the point at infinity as a special case - infty = self.parent()((1,0)) - - if basepoint == Infinity or basepoint == infty: - if self == infty or other == infty: - return infty - dist = self._custom_abs(self.center()[0] - other.center()[0]) - maximum = max(dist, self.radius(), other.radius()) - #optimize for when self or other are type II - if maximum == self.radius() and self.type_of_point() == 2: - return self.parent()(self.center(), power=self.power()) - if maximum == other.radius() and other.type_of_point() == 2: - return self.parent()(self.center(), power=other.power()) - return self.parent()(self.center(), maximum) - - if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError("basepoint must be a point of the same Berkovich projective line") - - #if the basepoint is type IV, we use the last disk in the approximation - if basepoint.type_of_point() == 4: - new_center = other.center()[-1] - new_radius = other.radius()[-1] - return self.join(other, self.parent()(new_center,new_radius)) - - if self == infty: - return other.join(basepoint) - if other == infty: - return self.join(basepoint) - - #since none of the self, other, and basepoint are infinity, we can now treat them - #as affine points - b_greater_than_s = basepoint.partial_order(self) - b_greater_than_o = basepoint.partial_order(other) - s_greater_than_o = self.partial_order(other) - - #we deal with all the cases where self and other are not comparable first - if s_greater_than_o == None: - if b_greater_than_o == None: - if b_greater_than_s == None: - #case where none of the points are comparable - dist_b_s = self._custom_abs(self.center()[0] - basepoint.center()[0]) - dist_b_o = self._custom_abs(other.center()[0] - basepoint.center()[0]) - return self.parent()(basepoint.center(),\ - min(max(dist_b_o,other.radius(),basepoint.radius()), \ - max(dist_b_s,self.radius(),basepoint.radius()))) - - #case where self and basepoint are comparable - else: - if b_greater_than_s: - return basepoint - else: - return self - - #case where other and basepoint are comparable - else: - if b_greater_than_o: - return basepoint - else: - return other - - #now the cases where self > other - elif s_greater_than_o: - if b_greater_than_s == None: - return self - if b_greater_than_s: - return self - if b_greater_than_o: - return basepoint - if b_greater_than_o == False: - return other - - #join is symmetric, so we flip self and other so that self > other - else: - return other.join(self,basepoint) - - def involution_map(self): - r""" - Returns the image of this point under the involution map. - - The involution map is the extension of the map ``z |-> 1/z`` - on `P^1(\CC_p)` to Berkovich space. - - OUTPUT: A point of the same Berkovich space. - - EXAMPLES: - - The involution map is 1/z on type I points:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(1/2) - sage: Q1.involution_map() - Type I point centered at (2 + O(3^20) : 1 + O(3^20)) - - :: - - sage: Q2 = B(0, 1/3) - sage: Q2.involution_map() - Type II point centered at (0 : 1 + O(3^20)) of radius 3^1 - - :: - - sage: Q3 = B(1/3, 1/3) - sage: Q3.involution_map() - Type II point centered at (3 + O(3^21) : 1 + O(3^20)) of radius 3^-3 - - TESTS:: - - sage: B = Berkovich_Cp_Projective(3) - sage: B((1,0)).involution_map() - Type I point centered at (0 : 1 + O(3^20)) - - :: - - sage: B(0).involution_map() - Type I point centered at (1 + O(3^20) : 0) - - :: - - sage: B(1/81, 1.5).involution_map() - Type III point centered at (3^4 + O(3^24) : 1 + O(3^20)) of radius 0.000228623685413809 - """ - infty = self.parent()((1,0)) - zero = self.parent()(0) - - if self.type_of_point() == 1: - if self == infty: - return zero - if self == zero: - return infty - return self.parent()(1/self.center()[0]) - - if self.type_of_point() in [2,3]: - zero_contained_in_self = self.partial_order(zero) - if zero_contained_in_self: - if self.type_of_point() == 2: - power = self.power() - return self.parent()(0, power=-power) - return self.parent()(0, 1/self.radius()) - return self.parent()(1/self.center()[0], self.radius()/(self._custom_abs(self.center()[0])**2)) - - new_center_lst = [] - new_radius_lst = [] - for i in range(len(self.center())): - berk_point = self.parent()(self.center()[i], self.radius()[i]) - zero_check = berk_point.partial_order(zero) - if zero_check: - new_center = 0 - new_radius = 1/self.radius()[i] - else: - new_center = 1/self.center()[i][0] - new_radius = self.radius()[i]/(self._custom_abs(self.center()[i][0])**2) - new_center_lst.append(new_center) - new_radius_lst.append(new_radius) - return self.parent()(new_center_lst, new_radius_lst) - - def contained_in_interval(self, start, end): - """ - Checks if this point is an element of the interval [``start``, ``end``]. - - INPUT: - - - ``start`` -- A point of the same Berkovich space as this point. - - ``end`` -- A point of the same Berkovich space as this point. - - OUTPUT: - - - ``True`` if this point is an element of [``start``, ``end``]. - - ``False`` otherwise. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 1) - sage: Q2 = B(2, 4) - sage: Q3 = B(1/3) - sage: Q2.contained_in_interval(Q1, Q3.join(Q1)) - False - - :: - - sage: Q4 = B(1/81, 1) - sage: Q2.contained_in_interval(Q1, Q4.join(Q1)) - True - - TESTS:: - - sage: B = Berkovich_Cp_Projective(3) - sage: infty = B((1,0)) - sage: zero = B(0) - sage: gauss = B(0,1) - sage: infty.contained_in_interval(zero, gauss) - False - - :: - - sage: Q1 = B(1,3) - sage: infty.contained_in_interval(gauss, Q1) - False - - :: - - sage: zero.contained_in_interval(infty, gauss) - False - - :: - - sage: gauss.contained_in_interval(zero, infty) - True - - :: - - sage: Q2 = B(81, 1/3) - sage: gauss.contained_in_interval(infty, Q2) - True - """ - if not isinstance(start, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") - if start.parent() != self.parent(): - raise ValueError("start must be a point of the same Berkovich space as this point") - if not isinstance(end, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") - if end.parent() != self.parent(): - raise ValueError("start must be a point of the same Berkovich space as this point") - - #we treat infinity as a special case - infty = self.parent()((1, 0)) - zero = self.parent()(ZZ(0)) - if self == infty: - if start == zero or end == zero: - return end == infty or start == infty - return (self.involution_map()).contained_in_interval(start.involution_map(),\ - end.involution_map()) - if start == infty or end == infty: - if self == zero: - return end == zero or start == zero - if start == zero or end == zero: - gauss = self.parent()(ZZ(0),ZZ(1)) - return self.contained_in_interval(start,gauss) or self.contained_in_interval(gauss,end) - return self.involution_map().contained_in_interval(start.involution_map(), \ - end.involution_map()) - join = start.join(end) - return join.partial_order(self) and (self.partial_order(start) \ - or self.partial_order(end)) - - def potential_kernel(self, other, basepoint): - """ - The potential kernel of this point with ``other``, - with basepoint ``basepoint``. - - The potential kernel is the hyperbolic distance - between ``basepoint`` and the join of this point - with ``other`` relative to ``basepoint``. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- A point of the same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(27, 1) - sage: Q2 = B(1/3, 2) - sage: Q3 = B(1/9, 1/2) - sage: Q3.potential_kernel(Q1, Q2) - 0.369070246428543 - """ - if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) - if other.parent() != self.parent(): - raise ValueError('other must be a point of the same projective Berkovich line') - if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError('basepoint must be a point of the same projective Berkovich line') - return basepoint.path_distance_metric(self.join(other, basepoint)) - - def spherical_kernel(self,other): - r""" - The spherical kernel of this point with ``other``. - - The spherical kernel is one possible extension of the spherical - distance on `P^1(\CC_p)` to the projective Berkovich line. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 2) - sage: Q2 = B(1/9, 1) - sage: Q1.spherical_kernel(Q2) - 0.500000000000000 - - :: - - sage: Q3 = B(2) - sage: Q3.spherical_kernel(Q3) - 0 - """ - if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) - if other.parent() != self.parent(): - raise ValueError('other must be a point of the same projective Berkovich line') - gauss_point = self.parent()(ZZ(0), ZZ(1)) - w = self.join(other,gauss_point) - dist = gauss_point.path_distance_metric(w) - if dist == Infinity: - return 0 - return self.prime()**(-1*dist) - - def diameter(self, basepoint=Infinity): - r""" - Generalized diameter function. - - If the basepoint is infinity, the diameter is equal to - the limit of the radii of the corresponding disks in `\CC_p`. - - If the basepoint is not infinity, the diameter - is the Hsia kernel of this point with itself at - basepoint ``basepoint``. - - INPUT: +from sage.schemes.projective.projective_space import is_ProjectiveSpace, ProjectiveSpace +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.number_fields import NumberFields +from sage.rings.integer_ring import ZZ +from sage.rings.padics.factory import Qp +from sage.rings.rational_field import QQ +from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal +from sage.rings.padics.generic_nodes import is_pAdicField +from sage.categories.topological_spaces import TopologicalSpaces - - ``basepoint`` -- (default = Infinity) A point of the same - Berkovich space as this point, or infinity. +def is_Berkovich(space): + """ + Checks if ``space`` is a Berkovich space. - OUTPUT: A real number or infinity. + OUTPUT: - EXAMPLES:: + - ``True`` if ``space`` is a Berkovich space. + - ``False`` otherwise. + """ + return isinstance(space, Berkovich) - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(1/81, 1) - sage: Q2 = B(1/3) - sage: Q1.diameter(Q2) - 0.00137174211248285 +def is_Berkovich_Cp(space): + """ + Checks if ``space`` is a Berkovich space over ``Cp``. - :: + OUTPUT: - sage: Q2.diameter(Q2) - +infinity - """ - if basepoint == Infinity: - return super().diameter() - else: - return self.Hsia_kernel(self, basepoint) + - ``True`` if ``space`` is a Berkovich space over ``Cp``. + - ``False`` otherwise. + """ + return isinstance(space, Berkovich_Cp) class Berkovich(UniqueRepresentation, Parent): """ @@ -2827,6 +357,30 @@ def _repr_(self): return "Affine Berkovich line over Cp(%s), with base %s" %(self.prime(),\ self.base()) + def _projective_space(self): + """ + Creates a projective Berkovich space with the same characteristics as this space. + + OUTPUT: A projective Berkovich space over ``Cp``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B._projective_space() + Projective Berkovich line over Cp(3) of precision 20 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(z^3 + 20) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Affine(A, ideal) + sage: B._projective_space() + Projective Berkovich line over Cp(3), with base Number Field + in a with defining polynomial z^3 + 20 + """ + return Berkovich_Cp_Projective(self.base_ring(), self.ideal()) + def _latex_(self): r""" LaTeX representation of this Berkovich Space. @@ -2972,6 +526,30 @@ def base_ring(self): """ return self.base().base_ring() + def _affine_space(self): + """ + Creates an affine Berkovich space with the same characteristics as this space. + + OUTPUT: An affine Berkovich space over ``Cp``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: B._affine_space() + Affine Berkovich line over Cp(3) of precision 20 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(z^3+20) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: B._affine_space() + Affine Berkovich line over Cp(3), with base Number Field in + a with defining polynomial z^3 + 20 + """ + return Berkovich_Cp_Affine(self.base_ring(), self.ideal()) + def _repr_(self): """ String representation of this Berkovich Space. @@ -3007,4 +585,4 @@ def _latex_(self): sage: latex(B) \text{Projective Berkovich line over } \Bold{C}_{3} """ - return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) + return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) \ No newline at end of file From 6e6a12b70309dc74137744e6329c86916cd88438 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 23 Jul 2020 12:31:16 -0400 Subject: [PATCH 069/379] 29844: added number field documentation --- .../schemes/berkovich/berkovich_cp_element.py | 27 ++--- src/sage/schemes/berkovich/berkovich_space.py | 109 ++++++++++++++++-- 2 files changed, 116 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index d574c006bcc..07b14fe9494 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -1,18 +1,20 @@ r""" -Berkovich Space over `\CC_p` +Elements of Berkovich space. -The Berkovich affine line is the set of seminorms on `\CC_p[x]`, -with the weakest topology that makes the map `| \cdot | \to |f|` continuous -for all `f \in \CC_p[x]`. The Berkovich projective line is the -one-point compactification of the Berkovich affine line. +:class:`Berkovich_Element` is abstract parent class for elements of any Berkovich space. -The two main classes are :class:`Berkovich_Cp_Affine` and -:class:`Berkovich_Cp_Projective`, which implement the affine and -projective lines, respectively. +:class:`Berkovich_Element_Cp_Affine` and :class:`Berkovich_Element_Cp_Projective` +implement elements of Berkovich space over `\CC_p` and `P^1(\CC_p)`. Elements are +determined by specific data and fall into one of the four following types: -:class:`Berkovich_Cp_Affine` and :class:`Berkovich_Cp_Projective` -take as input one of the following: the prime `p`, a finite -extension of `\QQ_p`, or a number field and a place. +- Type I points are represented by a center. + +- Type II points are represented by a center and a rational power of `p`. + +- Type III points are represented by a center and a non-negative real radius. + +- Type IV points are represented by a finite list of centers and a finite list of + non-negative radii. AUTHORS: @@ -44,7 +46,6 @@ from sage.rings.integer_ring import ZZ from sage.rings.infinity import Infinity - class Berkovich_Element(Element): """ The parent class for any element of a Berkovich space @@ -968,7 +969,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): and a rational power of `p`. - Type III points are represented by a center in the ``base`` of the parent Berkovich space, - and a radius in `[0,\infty)`. + and a radius, a real number in `[0,\infty)`. - Type IV points are represented by a finite list of centers in the ``base`` of the parent Berkovich space and a finite list of radii in `[0,\infty)`. Type IV points can be created diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 69f99229dd8..13bf1280429 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -237,12 +237,18 @@ def __ne__(self,right): class Berkovich_Cp_Affine(Berkovich_Cp): r""" - The Berkovich Affine line over `\CC_p`. + The Berkovich affine line over `\CC_p`. - The Berkovich Affine line is the set of seminorms on `\CC_p[x]`, + The Berkovich affine line is the set of seminorms on `\CC_p[x]`, with the weakest topology such that the map `| \cdot | \to |f|` is continuous for all `f \in \CC_p[x]`. + We can represent the Berkovich affine line in two seperate ways: + either using a p-adic field to represent elements or using + a number field to represent elements while storing an ideal + of the ring of integers of the number field, which specifies + an embedding of the number field into `\CC_p`. See the examples. + INPUT: - ``base`` -- Three cases: @@ -265,7 +271,21 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: B = Berkovich_Cp_Affine(3); B Affine Berkovich line over Cp(3) of precision 20 - Initializing by passing in ``Qp`` looks the same:: + We can create elements:: + + sage: Q1 = B(-2); Q1 + Type I point centered at 1 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + + 2*3^14 + 2*3^15 + 2*3^16 + 2*3^17 + 2*3^18 + 2*3^19 + O(3^20) + + :: + + sage: Q2 = B(1, 2); Q2 + Type III point centered at 1 + O(3^20) of radius 2.00000000000000 + + For details on element creation, see the documentation + of :class:`Berkovich_Element_Cp_Affine`. Initializing by + passing in `\QQ_p` looks the same:: sage: B = Berkovich_Cp_Affine(Qp(3)); B Affine Berkovich line over Cp(3) of precision 20 @@ -275,16 +295,24 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: B = Berkovich_Cp_Affine(Qp(3, 1)); B Affine Berkovich line over Cp(3) of precision 1 - sage: Q1 = B(1/2); Q1 + sage: Q2 = B(1/2); Q2 Type I point centered at 2 + O(3) - Note that this point has very low precision, as B was initialized + Note that this point has very low precision, as ``B`` was initialized with a padic field of capped-relative precision one. For high precision, pass in a high precision padic field:: sage: B = Berkovich_Cp_Affine(Qp(3, 1000)); B Affine Berkovich line over Cp(3) of precision 1000 + Points of Berkovich space can be created from points of + extensions of `\QQ_p`:: + + sage: B = Berkovich_Cp_Affine(3) + sage: A. = Qp(3).extension(x^3-3) + sage: B(a) + Type I point centered at a + O(a^61) + For exact computation, a number field can be used:: sage: R. = QQ[] @@ -293,6 +321,30 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: B = Berkovich_Cp_Affine(A, ideal); B Affine Berkovich line over Cp(3), with base Number Field in a with defining polynomial x^3 + 20 + + Number fields a major advantage of exact computation. + + Number fields also have added functionality. Arbitrary extensions of + `\QQ` are supported, while there is currently limited functionality + for extensions of `\QQ_p`. As seen above, constructing a Berkovich + space backed by a number field requires specifying an ideal of the + ring of integers of the number field. Specifying the ideal uniquely + specifies an embedding of the number field into `\CC_p`. + + Unlike in the case where Berkovich space is backed by a p-adic + field, any point of a Berkovich space backed by a number field + must be centered at a point of that number field:: + + sage: R. = QQ[] + sage: A. = NumberField(x^3 + 20) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Affine(A, ideal) + sage: C. = NumberField(x^2 + 1) + sage: B(c) + Traceback (most recent call last): + ... + ValueError: could not convert c to Number Field in a + with defining polynomial x^3 + 20 """ Element = Berkovich_Element_Cp_Affine @@ -398,7 +450,13 @@ class Berkovich_Cp_Projective(Berkovich_Cp): The Berkovich projective line over `\CC_p`. The Berkovich projective line is the one-point compactification - of the Berkovich Affine line. + of the Berkovich affine line. + + We can represent the Berkovich projective line in two seperate ways: + either using a p-adic field to represent elements or using + a number field to represent elements while storing an ideal + of the ring of integers of the number field, which specifies + an embedding of the number field into `\CC_p`. See the examples. INPUT: @@ -423,7 +481,21 @@ class Berkovich_Cp_Projective(Berkovich_Cp): sage: B = Berkovich_Cp_Projective(3); B Projective Berkovich line over Cp(3) of precision 20 - Initializing by passing in a padic space looks the same:: + Elements can be constructed:: + + sage: B(1/2) + Type I point centered at (2 + 3 + 3^2 + 3^3 + 3^4 + 3^5 + + 3^6 + 3^7 + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20) : 1 + O(3^20)) + + :: + + sage: B(2, 1) + Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 + + For details about element construction, see the documentation of + :class:`Berkovich_Element_Cp_Projective`. Initializing a Berkovich projective + line by passing in a padic space looks the same:: sage: B = Berkovich_Cp_Projective(Qp(3)); B Projective Berkovich line over Cp(3) of precision 20 @@ -451,6 +523,29 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Number fields have the benefit that computation is exact, but lack support for all of `\CC_p`. + + Number fields also have the advantage of added functionality, + as arbitrary extensions of `\QQ` can be constructed while + there is currently limited functionality for extensions of `\QQ_p`. + As seen above, constructing a Berkovich space backed by a number + field requires specifying an ideal of the ring of integers + of the number field. Specifying the ideal uniquely specifies + an embedding of the number field into `\CC_p`. + + Unlike in the case where Berkovich space is backed by a p-adic + field, any point of a Berkovich space backed by a number field + must be centered at a point of that number field:: + + sage: R. = QQ[] + sage: A. = NumberField(x^3 + 20) + sage: ideal = A.prime_above(3) + sage: B = Berkovich_Cp_Projective(A, ideal) + sage: C. = NumberField(x^2 + 1) + sage: B(c) + Traceback (most recent call last): + ... + TypeError: could not convert c to Projective Space + of dimension 1 over Number Field in a with defining polynomial x^3 + 20 """ Element = Berkovich_Element_Cp_Projective From 157fe3b1561f884de0b60d7b1cf4f2f91d7ecb54 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 24 Jul 2020 11:22:33 -0400 Subject: [PATCH 070/379] 29844: better hash --- src/sage/schemes/berkovich/berkovich_cp_element.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 07b14fe9494..76d0fd2f339 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -1277,10 +1277,10 @@ def __hash__(self): True """ if self.type_of_point() == 1: - return hash(str(self.center())) + return hash(self.center()) elif self.type_of_point() == 4: raise NotImplementedError('hash not defined for type IV points') - return hash(str(self.radius())) + return hash(self.radius()) def partial_order(self,other): r""" @@ -1898,10 +1898,10 @@ def __hash__(self): True """ if self.type_of_point() == 1: - return hash(str(self.center())) + return hash(self.center()) elif self.type_of_point() == 4: raise ValueError('hash not defined for type IV points') - return hash(str(self.radius())) + return hash(self.radius()) def partial_order(self,other): r""" From 65b98b1fe05000e5302cf9137e26009913af77ca Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 24 Jul 2020 16:43:58 -0400 Subject: [PATCH 071/379] 29844: added lt and gt functions --- .../schemes/berkovich/berkovich_cp_element.py | 441 +++++++++++------- src/sage/schemes/berkovich/berkovich_space.py | 48 -- 2 files changed, 270 insertions(+), 219 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 76d0fd2f339..7554c2287ac 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -249,7 +249,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) else: #make sure the center is in the appropriate number field - if not(center in self._base_space): + if center.parent() == self._base_space: try: center = (self._base_space)(center) except (TypeError, ValueError) as e: @@ -278,31 +278,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= else: raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ "Berkovich_Element_Cp directly" ) - return #the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type if error_check: - if space_type == 'affine': - if self._base_type == 'padic field': - #make sure the center is in Cp - if not isinstance(center, pAdicGenericElement): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise TypeError("could not convert %s to %s" %(center, self._base_space)) - elif not is_pAdicField(center.parent()): - #center is padic, not but an element of a padic field. we convert to padic field - center = (center.parent().fraction_field())(center) - if (center.parent()).prime() != self._p: - raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) - else: - #make sure the center is in the appropriate number field - if not(center in self._base_space): - try: - center = (self._base_space)(center) - except (TypeError, ValueError) as e: - raise ValueError('could not convert %s to %s' %(center, self._base_space)) - elif space_type == "projective": + if space_type == "projective": if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) @@ -337,6 +316,26 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= "a point of %s" %(center.scheme())) #since we are over a field, we normalize coordinates center.normalize_coordinates() + elif space_type == 'affine': + if self._base_type == 'padic field': + #make sure the center is in Cp + if not isinstance(center, pAdicGenericElement): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise TypeError("could not convert %s to %s" %(center, self._base_space)) + elif not is_pAdicField(center.parent()): + #center is padic, not but an element of a padic field. we convert to padic field + center = (center.parent().fraction_field())(center) + if (center.parent()).prime() != self._p: + raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) + else: + #make sure the center is in the appropriate number field + if not(center.parent() == self._base_space): + try: + center = (self._base_space)(center) + except (TypeError, ValueError) as e: + raise ValueError('could not convert %s to %s' %(center, self._base_space)) else: raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ "Berkovich_Element_Cp directly") @@ -774,8 +773,8 @@ def small_metric(self, other): if self.parent() != other.parent(): raise ValueError('other must be a point of the same Berkovich space') gauss = self.parent()(RR(0), RR(1)) - g_greater_than_s = gauss.partial_order(self) - g_greater_than_o = gauss.partial_order(other) + g_greater_than_s = gauss.gt(self) + g_greater_than_o = gauss.gt(other) if g_greater_than_s and g_greater_than_o: return 2*(self.join(other,gauss).diameter()) - self.diameter() - other.diameter() if not g_greater_than_s: @@ -1162,7 +1161,8 @@ def as_projective_point(self): Type IV point of precision 20 with centers given by (1 + O(5^20))/((1 + O(5^20))*t) and radii given by 40.0000000000000*pi/x """ - new_space = self.parent()._projective_space() + from sage.schemes.berkovich.berkovich_space import Berkovich_Cp_Projective + new_space = Berkovich_Cp_Projective(self.parent().base_ring(), self.parent().ideal()) if self.type_of_point() == 1: return new_space(self.center()) elif self.type_of_point() == 2: @@ -1282,12 +1282,12 @@ def __hash__(self): raise NotImplementedError('hash not defined for type IV points') return hash(self.radius()) - def partial_order(self,other): + def lt(self, other): r""" - The standard partial order on Berkovich space. + Returns ``True`` if this point is less than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of - the corresponding disks in `\CC_p`. + the corresponding disks in ``Cp``. For example, let x and y be points of type II or III. If x has center `c_1` and radius `r_1` and y has center @@ -1300,41 +1300,136 @@ def partial_order(self,other): OUTPUT: - - ``True`` -- If self > other in the standard partial order. - - ``False`` -- If self < other in the standard partial order. - - ``None`` -- If the two points are not comparable. + - ``True`` -- If this point is less than ``other`` in the standard partial order. + - ``False`` -- Otherwise. EXAMPLES:: - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2, 4) - sage: Q2 = B(2, 6) - sage: Q1.partial_order(Q2) + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(5, 0.5) + sage: Q2 = B(5, 1) + sage: Q1.lt(Q2) + True + + :: + + sage: Q3 = B(1) + sage: Q1.lt(Q3) + False + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(5) + sage: Q1.lt(Q1) False :: - sage: Q3 = B(1/2) - sage: Q1.partial_order(Q3) + sage: Q2 = B([4, 1/3], [5, 1]) + sage: Q1.lt(Q2) + False + + :: + + sage: Q4 = B(0, 1) + sage: Q1.lt(Q4) True :: - sage: Q4 = B(1/81, 1) - sage: Q4.partial_order(Q1) is None + sage: Q2.lt(Q4) + False + """ + if not isinstance(other, Berkovich_Element_Cp_Affine): + raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same projective Berkovich space') + + if self == other: + return False + if other.type_of_point() in [1, 4]: + return False + + if self.type_of_point() == 4: + center = self.center()[-1] + dist = self._custom_abs(other.center() - center) + return dist <= other.radius() and self.radius()[-1] <= other.radius() + else: + dist = self._custom_abs(self.center() - other.center()) + return dist <= other.radius() and self.radius() <= other.radius() + + def gt(self, other): + r""" + Returns ``True`` if this point is greater than ``other`` in the standard partial order. + + Roughly, the partial order corresponds to containment of + the corresponding disks in `\CC_p`. + + For example, let x and y be points of type II or III. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2)` in `\CC_p`. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: + + - ``True`` -- If this point is greater than ``other`` in the standard partial order. + - ``False`` -- Otherwise. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Affine(QQ, 3) + sage: Q1 = B(5, 3) + sage: Q2 = B(5, 1) + sage: Q1.gt(Q2) True :: - sage: Q4.partial_order(Q3) is None + sage: Q3 = B(1/27) + sage: Q1.gt(Q3) + False + + TESTS:: + + sage: B = Berkovich_Cp_Affine(QQ, 3) + sage: Q1 = B(5) + sage: Q1.gt(Q1) + False + + :: + + sage: Q2 = B(0, 1) + sage: Q1.gt(Q2) + False + + :: + + sage: Q3 = B([0, 3], [5, 1]) + sage: Q2.gt(Q3) True """ - #error check, then convert to projective berkovich space to do the partial order if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) if self.parent() != other.parent(): - raise ValueError('other must be a point of the same affine Berkovich space') - return self.as_projective_point().partial_order(other.as_projective_point()) + raise ValueError('other must be a point of the same projective Berkovich space') + + if self == other: + return False + if self.type_of_point() in [1, 4]: + return False + + if other.type_of_point() == 4: + center = other.center()[-1] + dist = self._custom_abs(self.center() - center) + return dist <= self.radius() and other.radius()[-1] <= self.radius() + else: + dist = self._custom_abs(self.center() - other.center()) + return dist <= self.radius() and other.radius() <= self.radius() def join(self, other, basepoint=Infinity): """ @@ -1457,7 +1552,7 @@ def involution_map(self): radius = self.radius() if self.type_of_point() in [2,3]: - zero_contained_in_self = self.partial_order(zero) + zero_contained_in_self = self.gt(zero) if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() @@ -1467,9 +1562,9 @@ def involution_map(self): new_center_lst = [] new_radius_lst = [] - for i in range(len(center)): + for i in range(len(self.center())): berk_point = self.parent()(self.center()[i], radius[i]) - zero_check = berk_point.partial_order(zero) + zero_check = berk_point.gt(zero) if zero_check: new_center = 0 new_radius = RR(1/radius[i]) @@ -1778,7 +1873,8 @@ def as_affine_point(self): """ if self.center()[1] == 0: raise ValueError('cannot convert infinity to affine Berkovich space') - new_space = self.parent()._affine_space() + from sage.schemes.berkovich.berkovich_space import Berkovich_Cp_Affine + new_space = Berkovich_Cp_Affine(self.parent().base_ring(), self.parent().ideal()) if self.type_of_point() in [1,2,3]: center = self.center()[0] if self.type_of_point() == 1: @@ -1903,9 +1999,9 @@ def __hash__(self): raise ValueError('hash not defined for type IV points') return hash(self.radius()) - def partial_order(self,other): + def lt(self, other): r""" - The standard partial order on Berkovich space. + Returns ``True`` if this point is less than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of the corresponding disks in ``Cp``. @@ -1921,149 +2017,149 @@ def partial_order(self,other): OUTPUT: - - ``True`` - If self => other in the standard partial order. - - ``False`` - If other > self in the standard partial order. - - ``None`` - If the two points are not comparable. + - ``True`` -- If this point is less than ``other`` in the standard partial order. + - ``False`` -- Otherwise. EXAMPLES:: - sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) - sage: Q1 = B(2, 4) - sage: Q2 = B(2, 6) - sage: Q1.partial_order(Q2) - False - - :: - - sage: Q3 = B(1/2) - sage: Q1.partial_order(Q3) + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(5, 0.5) + sage: Q2 = B(5, 1) + sage: Q1.lt(Q2) True :: - sage: Q4 = B(1/81, 1) - sage: Q4.partial_order(Q1) == None - True - - We check infinity works in the partial order:: - - sage: Q5 = B((1,0)) - sage: Q6 = B(3, 3) - sage: Q6.partial_order(Q5) - True + sage: Q3 = B(1) + sage: Q1.lt(Q3) + False TESTS:: sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B((1,0)) - sage: Q2 = B(0) - sage: Q1.partial_order(Q2) == None - True + sage: Q1 = B(5) + sage: Q1.lt(Q1) + False :: - sage: Q2.partial_order(Q2) - True + sage: Q2 = B([4, 1/3], [5, 1]) + sage: Q1.lt(Q2) + False :: - sage: Q3 = B(2) - sage: Q2.partial_order(Q3) == None + sage: Q3 = B((1,0)) + sage: Q4 = B(0, 1) + sage: Q3.lt(Q4) True :: - sage: Q4 = B(1/27, 1) - sage: Q4.partial_order(Q2) == None + sage: Q1.lt(Q4) True :: - sage: Q2.partial_order(Q4) == None - True + sage: Q2.lt(Q4) + False + """ + if not isinstance(other, Berkovich_Element_Cp_Projective): + raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + if self.parent() != other.parent(): + raise ValueError('other must be a point of the same projective Berkovich space') - :: + if self == other: + return False + if other.type_of_point() in [1, 4]: + return False - sage: Q5 = B(0,1) - sage: Q2.partial_order(Q5) - False + # if this point is infinity, we apply the involution map + infty = self.parent()((1,0)) + if self == infty: + newself = self.involution_map() + newother = other.involution_map() + return newself.lt(newother) - :: + if self.type_of_point() == 4: + center = self.center()[-1] + dist = self._custom_abs(other.center()[0] - center[0]) + return dist <= other.radius() and self.radius()[-1] <= other.radius() + else: + dist = self._custom_abs(self.center()[0] - other.center()[0]) + return dist <= other.radius() and self.radius() <= other.radius() - sage: Q6 = B([3,2],[4,1/27]) - sage: Q4.partial_order(Q6) == None - True + def gt(self, other): + r""" + Returns ``True`` if this point is greater than ``other`` in the standard partial order. - :: + Roughly, the partial order corresponds to containment of + the corresponding disks in ``Cp``. - sage: Q5.partial_order(Q6) - True + For example, let x and y be points of type II or III. + If x has center `c_1` and radius `r_1` and y has center + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` + is a subset of `D(c_2,r_2)` in `\CC_p`. - :: + INPUT: - sage: Q7 = B(0,1/27) - sage: Q5.partial_order(Q7) + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: + + - ``True`` -- If this point is greater than ``other`` in the standard partial order. + - ``False`` -- Otherwise. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(QQ, 3) + sage: Q1 = B(5, 3) + sage: Q2 = B(5, 1) + sage: Q1.gt(Q2) True :: - sage: Q8 = B(0, 2) - sage: Q5.partial_order(Q8) + sage: Q3 = B(1/27) + sage: Q1.gt(Q3) + False + + TESTS:: + + sage: B = Berkovich_Cp_Projective(QQ, 3) + sage: Q1 = B(5) + sage: Q1.gt(Q1) False :: - sage: Q9 = B(1/9,10) - sage: Q5.partial_order(Q9) + sage: Q2 = B(0, 1) + sage: Q1.gt(Q2) False + + :: + + sage: Q3 = B([0, 3], [5, 1]) + sage: Q2.gt(Q3) + True """ if not isinstance(other, Berkovich_Element_Cp_Projective): raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same projective Berkovich space') - #if self or other is infinity, we apply the involution map - infty = self.parent()((1,0)) - zero = self.parent()(0) - if self == infty or other == infty: - if self == zero or other == zero: - return None - newself = self.involution_map() - newother = other.involution_map() - return newself.partial_order(newother) + if self == other: + return False + if self.type_of_point() in [1, 4]: + return False - if self.type_of_point() == 1: - if other.type_of_point() in [1,4]: - if self == other: - return True - return None - else: - s_less_than_o = other.partial_order(self) - if s_less_than_o == None: - return None - return not s_less_than_o + if other.type_of_point() == 4: + center = other.center()[-1] + dist = self._custom_abs(self.center()[0] - center[0]) + return dist <= self.radius() and other.radius()[-1] <= self.radius() else: - if other.type_of_point() == 1: - dist = self._custom_abs(self.center()[0] - other.center()[0]) - if dist <= self.radius(): - return True - return None - elif other.type_of_point() == 4: - center = other.center()[-1] - dist = self._custom_abs(self.center()[0] - center[0]) - if dist <= self.radius() and other.radius()[-1] <= self.radius(): - return True - return None - else: - dist = self._custom_abs(self.center()[0]-other.center()[0]) - if dist <= self.radius(): - if other.radius() <= self.radius(): - return True - return False - else: - if dist <= other.radius(): - return False - return None + dist = self._custom_abs(self.center()[0] - other.center()[0]) + return dist <= self.radius() and other.radius() <= self.radius() def join(self, other, basepoint=Infinity): """ @@ -2176,53 +2272,54 @@ def join(self, other, basepoint=Infinity): if basepoint.type_of_point() == 4: new_center = other.center()[-1] new_radius = other.radius()[-1] - return self.join(other, self.parent()(new_center,new_radius)) + return self.join(other, self.parent()(new_center, new_radius)) if self == infty: return other.join(basepoint) if other == infty: return self.join(basepoint) - #since none of the self, other, and basepoint are infinity, we can now treat them - #as affine points - b_greater_than_s = basepoint.partial_order(self) - b_greater_than_o = basepoint.partial_order(other) - s_greater_than_o = self.partial_order(other) + b_ge_s = basepoint.gt(self) or basepoint == self + b_lt_s = basepoint.lt(self) + b_ge_o = basepoint.gt(other) or basepoint == other + b_lt_o = basepoint.lt(other) + s_ge_o = self.gt(other) or self == other + s_lt_o = self.lt(other) #we deal with all the cases where self and other are not comparable first - if s_greater_than_o == None: - if b_greater_than_o == None: - if b_greater_than_s == None: + if not (s_lt_o or s_ge_o): + if not (b_ge_o or b_lt_o): + if not (b_ge_s or b_lt_s): #case where none of the points are comparable dist_b_s = self._custom_abs(self.center()[0] - basepoint.center()[0]) dist_b_o = self._custom_abs(other.center()[0] - basepoint.center()[0]) - return self.parent()(basepoint.center(),\ - min(max(dist_b_o,other.radius(),basepoint.radius()), \ - max(dist_b_s,self.radius(),basepoint.radius()))) + return self.parent()(basepoint.center(), \ + min(max(dist_b_o, other.radius(), basepoint.radius()), \ + max(dist_b_s, self.radius(), basepoint.radius()))) #case where self and basepoint are comparable else: - if b_greater_than_s: + if b_ge_s: return basepoint else: return self #case where other and basepoint are comparable else: - if b_greater_than_o: + if b_ge_o: return basepoint else: return other #now the cases where self > other - elif s_greater_than_o: - if b_greater_than_s == None: + elif s_ge_o: + if not (b_ge_s or b_lt_s): return self - if b_greater_than_s: + if b_ge_s: return self - if b_greater_than_o: + if b_ge_o: return basepoint - if b_greater_than_o == False: + if b_lt_o: return other #join is symmetric, so we flip self and other so that self > other @@ -2286,7 +2383,7 @@ def involution_map(self): return self.parent()(1/self.center()[0]) if self.type_of_point() in [2,3]: - zero_contained_in_self = self.partial_order(zero) + zero_contained_in_self = self.gt(zero) if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() @@ -2298,7 +2395,7 @@ def involution_map(self): new_radius_lst = [] for i in range(len(self.center())): berk_point = self.parent()(self.center()[i], self.radius()[i]) - zero_check = berk_point.partial_order(zero) + zero_check = berk_point.gt(zero) if zero_check: new_center = 0 new_radius = 1/self.radius()[i] @@ -2369,12 +2466,12 @@ def contained_in_interval(self, start, end): sage: gauss.contained_in_interval(infty, Q2) True """ - if not isinstance(start, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") + if not isinstance(start, Berkovich_Element_Cp_Projective): + raise TypeError("start must be a point of Berkovich space") if start.parent() != self.parent(): raise ValueError("start must be a point of the same Berkovich space as this point") - if not isinstance(end, Berkovich_Element_Cp): - raise ValueError("start must be a point of Berkovich space") + if not isinstance(end, Berkovich_Element_Cp_Projective): + raise TypeError("start must be a point of Berkovich space") if end.parent() != self.parent(): raise ValueError("start must be a point of the same Berkovich space as this point") @@ -2395,8 +2492,10 @@ def contained_in_interval(self, start, end): return self.involution_map().contained_in_interval(start.involution_map(), \ end.involution_map()) join = start.join(end) - return join.partial_order(self) and (self.partial_order(start) \ - or self.partial_order(end)) + j_ge_s = join.gt(self) or join == self + s_ge_start = self.gt(start) or self == start + s_ge_end = self.gt(end) or self == end + return j_ge_s and (s_ge_end or s_ge_start) def potential_kernel(self, other, basepoint): """ diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 13bf1280429..6a0e8da2580 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -409,30 +409,6 @@ def _repr_(self): return "Affine Berkovich line over Cp(%s), with base %s" %(self.prime(),\ self.base()) - def _projective_space(self): - """ - Creates a projective Berkovich space with the same characteristics as this space. - - OUTPUT: A projective Berkovich space over ``Cp``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: B._projective_space() - Projective Berkovich line over Cp(3) of precision 20 - - :: - - sage: R. = QQ[] - sage: A. = NumberField(z^3 + 20) - sage: ideal = A.prime_above(3) - sage: B = Berkovich_Cp_Affine(A, ideal) - sage: B._projective_space() - Projective Berkovich line over Cp(3), with base Number Field - in a with defining polynomial z^3 + 20 - """ - return Berkovich_Cp_Projective(self.base_ring(), self.ideal()) - def _latex_(self): r""" LaTeX representation of this Berkovich Space. @@ -621,30 +597,6 @@ def base_ring(self): """ return self.base().base_ring() - def _affine_space(self): - """ - Creates an affine Berkovich space with the same characteristics as this space. - - OUTPUT: An affine Berkovich space over ``Cp``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: B._affine_space() - Affine Berkovich line over Cp(3) of precision 20 - - :: - - sage: R. = QQ[] - sage: A. = NumberField(z^3+20) - sage: ideal = A.prime_above(3) - sage: B = Berkovich_Cp_Projective(A, ideal) - sage: B._affine_space() - Affine Berkovich line over Cp(3), with base Number Field in - a with defining polynomial z^3 + 20 - """ - return Berkovich_Cp_Affine(self.base_ring(), self.ideal()) - def _repr_(self): """ String representation of this Berkovich Space. From 74c169beee44c04c0659033d3db283ef84749763 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 24 Jul 2020 17:26:24 -0400 Subject: [PATCH 072/379] 29844: added references --- src/doc/en/reference/references/index.rst | 7 +++++++ src/sage/schemes/berkovich/berkovich_cp_element.py | 8 ++++++-- src/sage/schemes/berkovich/berkovich_space.py | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4df7644a2eb..4d8c95f4312 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -614,6 +614,9 @@ REFERENCES: Annals of Mathematics, Second Series, Vol. 29, No. 1/4 (1927 - 1928), pp. 38-46 +.. [Ben2019] Benedetto, Robert L. Dynamics in one non-archimedean variable. + Graduate Studies in Mathematics, Volume 198. 2019. + .. [Benasque2009] Fernando Rodriguez Villegas, *The L-function of the quintic*, http://users.ictp.it/~villegas/hgm/benasque-2009-report.pdf @@ -1001,6 +1004,10 @@ REFERENCES: *Reverse-Engineering the S-Box of Streebog, Kuznyechik and STRIBOBr1*; in EuroCrypt'16, pp. 372-402. +.. [BR2010] Matthew Baker and Robert Rumely. Potential theory and dynamics on the + Berkovich projective line. Mathematical Surveys and Monographs, + Volumne 159. 2010. + .. [Brandes01] Ulrik Brandes, A faster algorithm for betweenness centrality, Journal of Mathematical Sociology 25.2 (2001): 163-177, diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 7554c2287ac..9ebe1add148 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -16,6 +16,9 @@ - Type IV points are represented by a finite list of centers and a finite list of non-negative radii. +For an exposition of Berkovich space over `\CC_p`, see Chapter 6 of [Ben2019]_. For a more +involved exposition, see Chapter 1 and 2 of [BR2010]_. + AUTHORS: - Alexander Galarraga (2020-06-22): initial implementation @@ -636,7 +639,7 @@ def path_distance_metric(self, other): On the set of type II, III and IV points, the path distance metric is a metric. Following Baker and Rumely, we extend the path distance metric to type I points `x`, `y` by `\rho(x,x) = 0` and `\rho(x,y) = - \infty`. + \infty`. See [BR2010]_. INPUT: @@ -1657,7 +1660,7 @@ def spherical_kernel(self,other): The spherical kernel is one possible extension of the spherical distance on `A^1(\CC_p)` to the Berkovich - Affine line. + Affine line. See [BR2010]_ for details. OUTPUT: A real number. @@ -2538,6 +2541,7 @@ def spherical_kernel(self,other): The spherical kernel is one possible extension of the spherical distance on `P^1(\CC_p)` to the projective Berkovich line. + See [BR2010]_ for details. INPUT: diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 6a0e8da2580..fb5f3410c3d 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -14,6 +14,9 @@ take as input one of the following: the prime `p`, a finite extension of `\QQ_p`, or a number field and a place. +For an exposition of Berkovich space over `\CC_p`, see Chapter 6 of [Ben2019]_. For a more +involved exposition, see Chapter 1 and 2 of [BR2010]_. + AUTHORS: - Alexander Galarraga (2020-06-22): initial implementation From 331e0587a8775c98fc2196a76e9608b444da41ad Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sat, 25 Jul 2020 23:31:49 -0400 Subject: [PATCH 073/379] added mobile to FinitePoset --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/posets/__init__.py | 1 + src/sage/combinat/posets/mobile.py | 155 ++++++++++++++++++ src/sage/graphs/generic_graph.py | 22 ++- src/sage/graphs/graph.py | 17 ++ 5 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 src/sage/combinat/posets/mobile.py diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index d8be446a41e..e3f93cc7d6e 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -189,6 +189,7 @@ Comprehensive Module list sage/combinat/posets/all sage/combinat/posets/cartesian_product sage/combinat/posets/d_complete + sage/combinat/posets/mobile sage/combinat/posets/elements sage/combinat/posets/forest sage/combinat/posets/hasse_diagram diff --git a/src/sage/combinat/posets/__init__.py b/src/sage/combinat/posets/__init__.py index 01e0e411a96..0ef76a3374b 100644 --- a/src/sage/combinat/posets/__init__.py +++ b/src/sage/combinat/posets/__init__.py @@ -14,6 +14,7 @@ - :ref:`sage.combinat.posets.linear_extensions` - :ref:`sage.combinat.posets.d_complete` - :ref:`sage.combinat.posets.forest` +- :ref:`sage.combinat.posets.mobile` - :ref:`sage.combinat.posets.incidence_algebras` - :ref:`sage.combinat.posets.cartesian_product` diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py new file mode 100644 index 00000000000..4f3b0f3a205 --- /dev/null +++ b/src/sage/combinat/posets/mobile.py @@ -0,0 +1,155 @@ +# **************************************************************************** +# Copyright (C) 2020 Stefan Grosser +# +# 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.combinat.posets.posets import Poset, FinitePoset +from sage.combinat.posets.d_complete import DCompletePoset +#from .linear_extensions import LinearExtensionsOfForest + +class MobilePoset(FinitePoset): + r""" + Mobile posets are an extension of d-complete posets which permit a determinant + formula for counting linear extensions. + + """ + + #_lin_ext_type = LinearExtensionsOfForest + _desc = 'Finite mobile poset' + + def _mobile_structure(self): + H = self._hasse_diagram + H_un = H.to_undirected() + max_elmts = H.sinks() + + # Compute plant, ribbon + ribbon = [] # In order list of elements on zigzag + + plant = (0, 0) # The cut edge separating the plant from the zigzag + + # Compute max element tree by merging shortest paths + + start = max_elmts[0] + + zigzag_elmts = set() + for m in max_elmts[1:]: + sp = G.shortest_path(start, m) + zigzag_elmts.add(sp) + + max_elmt_graph = H.subgraph(zigzag_elmts) + G = max_elmt_graph.to_undirected() + + if G.is_path(): + # Check if there is a plant by seeing if there is more than one acyclic path to the next max + ends = max_elmt_graph.vertices_with_degree(1) + + # Form ribbon + ribbon = G.shortest_path(ends[0], ends[1]) + + for end in ends: + path = [] + nextElmt = end + # Get next maximal element in zigzag + while True: + path.append(nextElmt) + nbrs = G.neighbors(nextElmt) + nextElmt = nbrs[0] if nbrs[0] != nextElmt else nbrs[1] + if nextElmt in max_elmts: + break + + # Find spot where we enter zigzag + foundPlant = False + + for i, v in enumerate(path): + if len(H_un.all_paths(v, nextElmt)) > 1: + foundPlant = True + continue + elif i == 0: + break + if foundPlant: + plant = (v, path[i-1]) + # Shorten zigzag + + endIndex = ribbon.index(end) + plantIndex = ribbon.index(v) + + if plantIndex < endIndex: + ribbon = ribbon[:plantIndex] + else: + ribbon = ribbon[plantIndex:] + + break + + if foundPlant: + break + + else: + # First check path counts between ends and deg3 vertex + # Then check if more than one max elmt on way to degree 3 vertex. + # Arbitrarily choose between ones with just 1 + + ends = max_elmt_graph.vertices_with_degree(1) + deg3 = max_elmt_graph.vertices_with_degree(3)[0] + + plantedEnd = None + for end in ends: + if H_un.all_paths(end, deg3) > 1: + plantedEnd = end + break + + if not plantedEnd is None: + path = H.shortest_path(deg3, plantedEnd) + plant = (path[0], path[1]) + + ends.remove(plantedEnd) + ribbon = max_elmt_graph.shortest_path(end[0], end[1]) + + else: + possible_plants = ends[:] + for end in ends: + path = G.shortest_path(end, deg3) + if not reduce(lambda x,y: x^y, map(lambda z: z in max_elmts, path)): + possible_plants.remove(end) + + plantedEnd = possible_plants[0] + ends.remove(plantedEnd) + ribbon = G.shortest_path(ends[0], ends[1]) + + plant = (deg3, G.shortest_path(deg3, plantedEnd)[1]) + + self._ribbon = ribbon + self._plant = plant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c5d8e81a2aa..1f0652c0af3 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12077,7 +12077,27 @@ def degree(self, vertices=None, labels=False): return next(self.degree_iterator(vertices, labels)) else: return list(self.degree_iterator(vertices, labels)) - + + def vertices_with_degree(self, degree): + r""" + Return a list of vertices with the indicated degree. + + EXAMPLES: + + sage: G = graphs.CompleteGraph(5) + sage: G.vertices_with_degree(5) + [0, 1, 2, 3, 4] + sage: P = graphs.PathGraph(5) + sage: P.vertices_with_degree(1) + [0, 19] + """ + vertices = [] + for v in self.vertex_iterator(): + if self.degree(v) == degree: + vertices.append(v) + + return vertices + def average_degree(self): r""" Return the average degree of the graph. diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index b340f6bde7a..f39a35cb028 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -4836,6 +4836,23 @@ def maximum_average_degree(self, value_only=True, solver=None, verbose=0): return g_mad.average_degree() else: return g_mad + + @doc_index("Graph properties") + def is_path(self): + r""" + Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). + + EXAMPLES: + + sage: G = graphs.PathGraph(5) + sage: G.is_path() + True + sage: H = graphs.CycleGraph(5) + sage: H.is_path() + False + """ + ds = self.degree_sequence() + return all(map(lambda x: x[0] == x[1], zip(ds, ([2] * (self.order() - 2) + ([1] * 2))))) @doc_index("Algorithmically hard stuff") def independent_set_of_representatives(self, family, solver=None, verbose=0): From a58c0d7ff6dc68f59e69717ed98b97c8630fa156 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 26 Jul 2020 01:50:28 -0400 Subject: [PATCH 074/379] fixed indentation and tests and merged develop --- src/sage/graphs/generic_graph.py | 4 ++-- src/sage/graphs/graph.py | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c9e31059609..cdcb4c9519e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12091,11 +12091,11 @@ def vertices_with_degree(self, degree): EXAMPLES: sage: G = graphs.CompleteGraph(5) - sage: G.vertices_with_degree(5) + sage: G.vertices_with_degree(4) [0, 1, 2, 3, 4] sage: P = graphs.PathGraph(5) sage: P.vertices_with_degree(1) - [0, 19] + [0, 4] """ vertices = [] for v in self.vertex_iterator(): diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index c1e574f4ca0..0021a45422e 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -4839,20 +4839,20 @@ def maximum_average_degree(self, value_only=True, solver=None, verbose=0): @doc_index("Graph properties") def is_path(self): - r""" - Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). - - EXAMPLES: - - sage: G = graphs.PathGraph(5) - sage: G.is_path() - True - sage: H = graphs.CycleGraph(5) - sage: H.is_path() - False - """ - ds = self.degree_sequence() - return all(map(lambda x: x[0] == x[1], zip(ds, ([2] * (self.order() - 2) + ([1] * 2))))) + r""" + Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). + + EXAMPLES: + + sage: G = graphs.PathGraph(5) + sage: G.is_path() + True + sage: H = graphs.CycleGraph(5) + sage: H.is_path() + False + """ + ds = self.degree_sequence() + return all(map(lambda x: x[0] == x[1], zip(ds, ([2] * (self.order() - 2) + ([1] * 2))))) @doc_index("Algorithmically hard stuff") def independent_set_of_representatives(self, family, solver=None, verbose=0): From 7555255d1f36e114affe73d46c6af602515c849b Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sun, 26 Jul 2020 13:11:14 +0200 Subject: [PATCH 075/379] cleaned up code; added docstring; added tests --- src/sage/combinat/designs/design_catalog.py | 4 + src/sage/combinat/designs/gen_quadrangles.pxd | 127 ------- src/sage/combinat/designs/gen_quadrangles.pyx | 324 ++++++++++++++++++ src/sage/matrix/matrix_space.py | 113 ------ 4 files changed, 328 insertions(+), 240 deletions(-) delete mode 100644 src/sage/combinat/designs/gen_quadrangles.pxd create mode 100644 src/sage/combinat/designs/gen_quadrangles.pyx diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index fafb01b3d07..a31545a6079 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -59,6 +59,7 @@ :meth:`~sage.combinat.designs.steiner_quadruple_systems.steiner_quadruple_system` :meth:`~sage.combinat.designs.block_design.projective_plane` :meth:`~sage.combinat.designs.biplane` + :meth:`~sage.combinat.designs.gen_quadrangles` And the :meth:`designs.best_known_covering_design_from_LJCR ` function @@ -115,3 +116,6 @@ lazy_import('sage.combinat.designs.orthogonal_arrays', 'OAMainFunctions', as_='orthogonal_arrays') + +lazy_import('sage.combinat.designs.gen_quadrangles', + ('generalised_quadrangle_with_spread', 'generalised_quadrangle_hermitian')) diff --git a/src/sage/combinat/designs/gen_quadrangles.pxd b/src/sage/combinat/designs/gen_quadrangles.pxd deleted file mode 100644 index d91a8540e69..00000000000 --- a/src/sage/combinat/designs/gen_quadrangles.pxd +++ /dev/null @@ -1,127 +0,0 @@ -def generalised_quadrangle_with_spread(const int s, const int t, existence=False, check=True): - r""" - Returns a pair (GQ,S) s.t. GQ is a generalised quadrangle of order (s,t) and S is a spread of GQ - """ - if s < 1 or t < 1: - if existence: return False - raise RuntimeError("No GQ of order ({},{}) exists".format(s,t)) - - if s == 1 and t == 1:#we have a square - if existence: return True - D = IncidenceStructure([[0,1],[1,2],[2,3],[3,0]]) - return (D,[[0,1],[2,3]]) - - if is_prime_power(s) and t == s*s: - if existence: return True - (GQ,S) = dual_GQ_ovoid(*generalised_quadrangle_hermitian(s)) - if check: - if not is_GQ_with_spread(GQ,S,s,t): - raise RuntimeError("Sage built a wrong GQ with spread") - return (GQ,S) - - if existence: return Unknown - raise RuntimeError("Sage can't build a GQ of order ({},{}) with a spread".format(s,t)) - -def is_GQ_with_spread(GQ,S,const int s, const int t): - r""" - Checks if GQ is a generalised quadrangle of order (s,t) and - checks that S is a spred of GQ - """ - res = GQ.is_generalised_quadrangle(parameters=True) - if res is False or res[0] != s or res[1] != t: - return False - - #check spread - points = set(GQ.ground_set()) - for line in S: - if not points.issuperset(line): - return False - points = points.difference(line) - - if points: - return False - - return True - -def dual_GQ_ovoid(GQ,O): - r""" - Computes the dual of GQ and returns the image of O under the dual map - """ - #we compute the dual of GQ and of O - - #GQ.ground_set()[i] becomes newBlocks[i] - #GQ.blocks()[i] becomes i - newBlocks = [ [] for _ in range(GQ.num_points())] - pointsToInt = { p: i for i,p in enumerate(GQ.ground_set()) } - - for i,b in enumerate(GQ.blocks()): - for p in b: - newBlocks[pointsToInt[p]].append(i) - - S = [ newBlocks[pointsToInt[p]] for p in O] - - D = IncidenceStructure(newBlocks) - return (D,S) - -def generalised_quadrangle_hermitian(const int q): - r""" - Construct the generalised quadrangle H(3,q^2) with an ovoid - The GQ has order (q^2,q) - """ - - GU = libgap.GU(4,q) - H = libgap.InvariantSesquilinearForm(GU)["matrix"] - Fq = libgap.GF(q*q) - zero = libgap.Zero(Fq) - one = libgap.One(Fq) - V = libgap.FullRowSpace(Fq,4) - - e1 = [one,zero,zero,zero] #isotropic point - assert( e1*H*e1 == zero, "e1 not isotropic") - - points = list(libgap.Orbit(GU,e1,libgap.OnLines)) #all isotropic points - pointInt = { x:(i+1) for i,x in enumerate(points) } #+1 because GAP starts at 1 - #points is the hermitian variety - - GUp = libgap.Action(GU, points, libgap.OnLines)#GU as permutation group of points - - e2 = [zero,one,zero,zero] - #we have totally isotropic line - line = V.Subspace([e1,e2]) - lineAsPoints = [libgap.Elements(libgap.Basis(b))[0] for b in libgap.Elements(line.Subspaces(1)) ] - line = libgap.Set([ pointInt[p] for p in lineAsPoints ]) - - lines = libgap.Orbit(GUp, line, libgap.OnSets)#all isotropic lines - - #to find ovoid, we embed H(3,q^2) in H(4,q^2) - #then embedding is (a,b,c,d) -> (a,b,0,c,d) [so we preserve isotropicity] - #then find a point in the latter and not in the former - #this point will be collinear in H(3,q^2) to all (and only) the points in a ovoid - W = libgap.FullRowSpace(Fq,5) - J = [ [0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]] - J = libgap(J) - if q%2 == 1: - (p,k) = is_prime_power(q,get_data=True) - a = (p-1)// 2 - aGap = zero - for i in range(a): aGap += one - p = [zero,one,one,aGap,zero] - else: - a = libgap.PrimitiveRoot(Fq)**(q-1) - p = [zero,one,a+one,a,zero] - - #now p is a point of H(4,q^2) - - #p' is collinear to p iff p'Jp^q = 0 - #note that p'Jp^q = bx^q + c where p' =(a,b,0,c,d) and p=(0,1,1,x,0) - #hece we have points (0,0,0,1); (0,1,c,a) for any a iff c^q+c = 0 (c = -x^q) - #and (1,b,c,x) for any x and any b (c= -bx^q) iff it is an iso point - #so we need only q^2 (for last case) +1 (for 2nd case) checks - ovoid = [] - xq = p[3]**q - for p2 in points: - if p2[1]*xq+p2[2] == zero: #p collinear to newP2 - ovoid.append(libgap(pointInt[p2])) - - D = IncidenceStructure(lines) - return (D,ovoid) diff --git a/src/sage/combinat/designs/gen_quadrangles.pyx b/src/sage/combinat/designs/gen_quadrangles.pyx new file mode 100644 index 00000000000..3ccb504368d --- /dev/null +++ b/src/sage/combinat/designs/gen_quadrangles.pyx @@ -0,0 +1,324 @@ +r""" +Database of generalised quadrangles + +This module implements some construction of generalised quadrangles +with spread. + +EXAMPLES:: + + sage: GQ, S = designs.generalised_quadrangle_with_spread(4, 16, check=False) + sage: GQ + Incidence structure with 325 points and 1105 blocks + sage: GQ2, O = designs.generalised_quadrangle_hermitian(4) + sage: GQ2 + Incidence structure with 1105 points and 325 blocks + sage: GQ3 = GQ.dual() + sage: set(GQ3._points) == set([x-1 for x in GQ2._points]) + True + sage: blocks2 = [set(map(lambda x: x-1, b)) for b in GQ2._blocks] + sage: all([set(b) in blocks2 for b in GQ3._blocks]) + True + +AUTHORS: + +- YOUR NAME (2005-01-03): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2013 IVO MAFFEI +# +# 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/ +# **************************************************************************** + + +def generalised_quadrangle_with_spread(const int s, const int t, + existence=False, check=True): + r""" + Construct a generalised quadrangle GQ of order `(s,t)` with a spread S. + + INPUT: + + - ``s, t`` -- integers; order of the generalised quadrangle + + - ``existence`` -- boolean; + + - ``check`` -- boolean; if ``True``, then Sage checks that the object built + is correct. (default: ``True``) + + OUTPUT: + + A pair `(GQ, S)` where `GQ` is a :class:`IncidenceStructure` representing + the generalised quadrangle and `S` is a list of blocks of `GQ` representing + the spread of `GQ`. + + EXAMPLES:: + + sage: t = designs.generalised_quadrangle_with_spread(3, 9) + sage: t[0] + Incidence structure with 112 points and 280 blocks + sage: designs.generalised_quadrangle_with_spread(5, 25, existence=True) + True + sage: (designs.generalised_quadrangle_with_spread(4, 16, check=False))[0] + Incidence structure with 325 points and 1105 blocks + sage: designs.generalised_quadrangle_with_spread(0, 2, existence=True) + False + + TESTS:: + + sage: GQ, S = designs.generalised_quadrangle_with_spread(2, 4) + sage: GQ + Incidence structure with 27 points and 45 blocks + sage: designs.generalised_quadrangle_with_spread(3, 4) + Traceback (most recent call last): + ... + RuntimeError: Sage can't build a GQ of order (3, 4) with a spread + sage: designs.generalised_quadrangle_with_spread(3, 4, existence=True) + Unknown + + """ + from sage.combinat.designs.incidence_structures import IncidenceStructure + from sage.misc.unknown import Unknown + from sage.arith.misc import is_prime_power + + if s < 1 or t < 1: + if existence: + return False + raise RuntimeError(f"No GQ of order ({s}, {t}) exists") + + if s == 1 and t == 1: # we have a square + if existence: return True + D = IncidenceStructure([[0, 1], [1, 2], [2, 3], [3, 0]]) + return (D, [[0, 1], [2, 3]]) + + if is_prime_power(s) and t == s*s: + if existence: + return True + (GQ, S) = dual_GQ_ovoid(*generalised_quadrangle_hermitian(s)) + if check: + if not is_GQ_with_spread(GQ, S, s=s, t=t): + raise RuntimeError("Sage built a wrong GQ with spread") + return (GQ, S) + + if existence: + return Unknown + raise RuntimeError( + f"Sage can't build a GQ of order ({s}, {t}) with a spread") + +def is_GQ_with_spread(GQ, S, s=None, t=None): + r""" + Check if GQ is a generalised quadrangle of order `(s,t)` and + check that S is a spread of GQ + + INPUT: + + - ``GQ`` -- IncidenceStructure; the incidence structure that is supposed to + be a generalised quadrangle + + - ``S`` -- iterable; the spread of ``GQ`` as an + iterable of the blocks of ``GQ`` + + - ``s, t`` -- integers (optional); if `(s,t)` are given, then we check that + ``GQ`` has order `(s,t)` + + EXAMPLES:: + + sage: from sage.combinat.designs.gen_quadrangles import * + sage: t = generalised_quadrangle_hermitian(3) + sage: is_GQ_with_spread(*t) + Traceback (most recent call last): + ... + TypeError: 'sage.libs.gap.element.GapElement_Integer' object is not iterable + sage: t = dual_GQ_ovoid(*t) + sage: is_GQ_with_spread(*t) + True + sage: is_GQ_with_spread(*t, s=3) + True + + TESTS:: + + sage: from sage.combinat.designs.gen_quadrangles import * + sage: t = generalised_quadrangle_hermitian(2) + sage: t = dual_GQ_ovoid(*t) + sage: is_GQ_with_spread(*t, s=2, t=4) + True + sage: is_GQ_with_spread(*t, s=2) + True + sage: is_GQ_with_spread(*t, s=3) + False + + """ + res = GQ.is_generalized_quadrangle(parameters=True) + if res is False \ + or (s != None and s != res[0]) \ + or (t != None and t != res[1]): + return False + + # check spread + points = set(GQ.ground_set()) + for line in S: + if not points.issuperset(line): + return False + points = points.difference(line) + + if points: + return False + + return True + +def dual_GQ_ovoid(GQ,O): + r""" + Compute the dual incidence structure of GQ + and return the image of `O` under the dual map + + INPUT: + + - ``GQ`` -- IncidenceStructure; the generalised quadrangle we want + the dual of + + - ``O`` -- iterable; the iterable of blocks we want to compute the dual + + OUTPUT: + + A pair ``(D, S)`` where ``D`` is the dual of ``GQ`` and + ``S`` is the dual of ``O`` + + EXAMPLES:: + + sage: from sage.combinat.designs.gen_quadrangles import dual_GQ_ovoid + sage: t = designs.generalised_quadrangle_hermitian(3) + sage: t[0].is_generalized_quadrangle(parameters=True) + (9, 3) + sage: t = dual_GQ_ovoid(*t) + sage: t[0].is_generalized_quadrangle(parameters=True) + (3, 9) + sage: all([x in t[0] for x in t[1]]) + True + + + TESTS:: + + sage: from sage.combinat.designs.gen_quadrangles import * + sage: t = designs.generalised_quadrangle_hermitian(2) + sage: t = dual_GQ_ovoid(*t) + sage: t[0].is_generalized_quadrangle(parameters=True) + (2, 4) + sage: is_GQ_with_spread(*t) + True + + """ + from sage.combinat.designs.incidence_structures import IncidenceStructure + + # GQ.ground_set()[i] becomes newBlocks[i] + # GQ.blocks()[i] becomes i + newBlocks = [[] for _ in range(GQ.num_points())] + pointsToInt = {p: i for i, p in enumerate(GQ.ground_set())} + + for i, b in enumerate(GQ.blocks()): + for p in b: + newBlocks[pointsToInt[p]].append(i) + + S = [newBlocks[pointsToInt[p]] for p in O] + + D = IncidenceStructure(newBlocks) + return (D, S) + +def generalised_quadrangle_hermitian(const int q): + r""" + Construct the generalised quadrangle `H(3,q^2)` with an ovoid + The GQ has order `(q^2,q)` + + INPUT: + + - ``q`` -- integer; a prime power + + OUTPUT: + + A pair ``(D, O)`` where ``D`` is an IncidenceStructure representing the + generalised quadrangle and ``O`` is a list of points of ``D`` which + constitute an ovoid of ``D`` + + EXAMPLES:: + + sage: t = designs.generalised_quadrangle_hermitian(4) + sage: t[0] + Incidence structure with 1105 points and 325 blocks + sage: len(t[1]) + 65 + sage: t[0].is_generalized_quadrangle() # long time + True + + TESTS:: + + sage: from sage.combinat.designs.gen_quadrangles import \ + is_GQ_with_spread, dual_GQ_ovoid + sage: t = designs.generalised_quadrangle_hermitian(3) + sage: t = dual_GQ_ovoid(*t) + sage: is_GQ_with_spread(*t, s=3, t=9) + True + sage: t = dual_GQ_ovoid(*(designs.generalised_quadrangle_hermitian(2))) + sage: t[0] + Incidence structure with 27 points and 45 blocks + sage: len(t[1]) + 9 + """ + from sage.libs.gap.libgap import libgap + from sage.combinat.designs.incidence_structures import IncidenceStructure + from sage.arith.misc import is_prime_power + + GU = libgap.GU(4,q) + H = libgap.InvariantSesquilinearForm(GU)["matrix"] + Fq = libgap.GF(q*q) + zero = libgap.Zero(Fq) + one = libgap.One(Fq) + V = libgap.FullRowSpace(Fq,4) + + e1 = [one,zero,zero,zero] # isotropic point + + points = list(libgap.Orbit(GU,e1,libgap.OnLines)) # all isotropic points + pointInt = { x:(i+1) for i,x in enumerate(points) } + # above we sum 1 because GAP starts at 1 + + GUp = libgap.Action(GU, points, libgap.OnLines) + + e2 = [zero,one,zero,zero] # another isotropic point + line = V.Subspace([e1,e2]) # totally isotropic line + lineAsPoints = [libgap.Elements(libgap.Basis(b))[0] + for b in libgap.Elements(line.Subspaces(1))] + line = libgap.Set([pointInt[p] for p in lineAsPoints]) + + lines = libgap.Orbit(GUp, line, libgap.OnSets) # all isotropic lines + # lines degines the GQ H(3,q^2) + + # to find an ovoid, we embed H(3,q^2) in H(4,q^2) + # the embedding is (a,b,c,d) -> (a,b,0,c,d) + # then we find a point in the latter and not in the former + # this point will be collinear (in H(3,q^2)) to all points in an ovoid + if q%2 == 1: + (p,k) = is_prime_power(q, get_data=True) + a = (p-1)// 2 + aGap = zero + for i in range(a): + aGap += one + p = [zero, one, one, aGap, zero] + else: + a = libgap.PrimitiveRoot(Fq)**(q-1) + p = [zero, one, a+one, a, zero] + + J = [[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]] + J = libgap(J) # matrix of the invariant form of GU(5,q) + + # p' is collinear to p iff p'Jp^q = 0 + # note that p'Jp^q = bx^q + c where p' = (a,b,0,c,d) and p = (0,1,y,x,0) + ovoid = [] + xq = p[3]**q + for p2 in points: + if p2[1]*xq + p2[2] == zero: # p collinear to p2 + ovoid.append(libgap(pointInt[p2])) + + D = IncidenceStructure(lines) + return (D, ovoid) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 12ae9bfa140..a8fc5e64493 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -1463,119 +1463,6 @@ def __iter__(self): for iv in sage.combinat.integer_vector.IntegerVectors(weight, number_of_entries, max_part=(order-1)): yield self(entries=[base_elements[i] for i in iv]) - - def symmetric_matrices(self, f, g=None ): - r""" - Return a generator of the matrices in this matrix space that satisfy: - `A[j,i] = f(A[i,j])` for `i != j` - `A[i,i] = g(A[i,i])` - - If the matrix space doesn't contains square matrices, then a - `ValueError` is raised. - - INPUT: - - - `f` -- function - - - `g` -- (optional) funcition; if it is None, then we assume `g=f`, - default value: `None`. - - EXAMPLES: - - - TESTS:: - - """ - if self.__nrows != self.__ncols: - raise ValueError("can't have symmetric matrices if they are not square") - if g == None: - g = f - - def make_symmetric( M ): - for i in range(M.nrows()): - for j in range(i+1, M.nrows()): - M[j,i] = f(M[i,j]) - - #Make sure that we can iterate over the base ring - base_ring = self.base_ring() - base_iter = iter(base_ring) - - nrows = self.__nrows - number_of_entries = nrows**2 - entries_in_upper_half = (nrows*(nrows-1))//2 - - #If the number of entries is zero, then just - #yield the empty matrix in that case and return - if number_of_entries == 0: - yield self(0) - return - - import sage.combinat.integer_vector - - if not base_ring.is_finite(): - #When the base ring is not finite, then we should go - #through and yield the matrices by "weight", which is - #the total number of iterations that need to be done - #on the base ring to reach the matrix. - base_elements = [ next(base_iter) ] - weight = 0 - while True: - for iv in sage.combinat.integer_vector.IntegerVectors(weight, entries_in_upper_half+nrows): - #Now we need to contruct the entries of the matrix so that is symmetric - #with respect to f and g - matrix_entries = [] #the upper half (with diagonal) entries of the matrix - length_of_row = nrows # == self.__ncols - valid_diagonal = True#if false, then we need a new integer vector - for _ in range(nrows): - #check diagonal - if base_elements[iv[0]] != g( base_elements[iv[0]] ): - valid_diagonal = False - break - #now construct the row - zeros = [0]*(nrows - length_of_row) - row = zeros + [base_elements[i] for i in iv[:length_of_row]] - matrix_entries.extend(row) #append row - - iv = iv[length_of_row:] - length_of_row -= 1 - - if valid_diagonal: - M = self(entries=matrix_entries) - #now make M symmetric with respect to f - make_symmetric(M) - yield M - #else iterate - weight += 1 - base_elements.append( next(base_iter) ) - else: - #In the finite case, we do a similar thing except that - #instead of checking if the diagonal is correct after creating the vector - #we can select all possible diagonal elements a priori - order = base_ring.order() - base_elements = list(base_ring) - diagonal_elements = [ x for x in base_elements if g(x) == x ] - number_diagonal_elements = len(diagonal_elements) - for weight1 in range((order-1)*entries_in_upper_half+1): - for iv2 in sage.combinat.integer_vector.IntegerVectors(weight1, entries_in_upper_half, max_part=(order-1)): - for weight2 in range((number_diagonal_elements-1)*nrows+1): - for dia in sage.combinat.integer_vector.IntegerVectors(weight2, nrows, max_part=(number_diagonal_elements-1)): - iv = iv2.clone() # iv is going to be changed within the next loop, so we keep a copy iv2 - #construct upper half matrix - matrix_entries = [] #entries of matrix with lower half 0 - length_of_row = nrows-1 - for r in range(nrows): - zeros = [0]*(nrows - length_of_row - 1) - row = zeros + [diagonal_elements[dia[r]]] + [base_elements[i] for i in iv[:length_of_row]] - matrix_entries.extend(row) - - iv = iv[length_of_row:] - length_of_row -= 1 - - M = self(entries=matrix_entries) - #make M symmetric - make_symmetric(M) - yield M - def __getitem__(self, x): """ Return a polynomial ring over this ring or the `n`-th element of this ring. From a44ccbdfedc00aa8eed1b10a7fb23cfaf0d4231e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 26 Jul 2020 19:55:08 +0200 Subject: [PATCH 076/379] trac #30188: review commit --- src/sage/graphs/base/boost_graph.pyx | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 17630761230..693481155b2 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2121,7 +2121,9 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al - ``g`` -- the input Sage graph - - ``vertex_list`` -- the list of vertices to compute shortest paths from + - ``vertex_list`` -- list (default: ``None``); list of vertices to compute + shortest paths from. By default (``None``), compute shortest paths from + all vertices. - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of @@ -2200,19 +2202,20 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if not isinstance(g, GenericGraph): raise TypeError("the input must be a Sage graph") - if not isinstance(vertex_list, list): - vertex_list = [vertex_list] + if vertex_list is None: + vertex_list = g - for v in vertex_list: - if v not in g: - raise ValueError(f"the starting vertex {v} is not in the graph") + else: + if not isinstance(vertex_list, list): + vertex_list = [vertex_list] - if vertex_list == None: - vertex_list = list(g) + for v in vertex_list: + if v not in g: + raise ValueError(f"the starting vertex {v} is not in the graph") # These variables are automatically deleted when the function terminates. - cdef v_index vi, vert - cdef dict int_to_v = dict(enumerate(g)) + cdef v_index vi, vert, pred + cdef list int_to_v = list(g) cdef dict v_to_int = {vv: vi for vi, vv in enumerate(g)} cdef result_distances result cdef BoostVecWeightedDiGraphU g_boost_dir @@ -2241,9 +2244,9 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al try: if weight_function is not None: - correct_type = type(weight_function(next(g.edge_iterator()))) + correct_type = type(weight_function(g.edges()[0])) elif g.weighted(): - correct_type = type(next(g.edge_iterator())[2]) + correct_type = type(g.edges()[0][2]) else: correct_type = int except StopIteration: @@ -2254,8 +2257,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if correct_type == RealNumber: correct_type = RR - distances = {} - predecessors = {} + cdef dict distances = {} + cdef dict predecessors = {} for v in vertex_list: vi = v_to_int[v] @@ -2276,10 +2279,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al raise RuntimeError("Dijkstra algorithm does not " "work with negative weights, " "use Bellman-Ford instead") - except RuntimeError: - raise RuntimeError("Dijkstra algorithm does not " - "work with negative weights, " - "use Bellman-Ford instead") + except RuntimeError as msg: + raise RuntimeError(msg) else: raise ValueError(f"unknown algorithm {algorithm!r}") else: @@ -2299,10 +2300,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al raise RuntimeError("Dijkstra algorithm does not " "work with negative weights, " "use Bellman-Ford instead") - except RuntimeError: - raise RuntimeError("Dijkstra algorithm does not " - "work with negative weights, " - "use Bellman-Ford instead") + except RuntimeError as msg: + raise RuntimeError(msg) else: raise ValueError(f"unknown algorithm {algorithm!r}") @@ -2313,7 +2312,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if result.distances[vert] != sys.float_info.max: w = int_to_v[vert] dist_v[w] = correct_type(result.distances[vert]) - pred_v[w] = int_to_v[result.predecessors[vert]] if result.predecessors[vert] != vert else None + pred = result.predecessors[vert] + pred_v[w] = int_to_v[pred] if pred != vert else None distances[v] = dist_v predecessors[v] = pred_v From 0e828961dacb8da81a5117a0b13da79560e8533c Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 27 Jul 2020 10:42:38 +0200 Subject: [PATCH 077/379] removed gap int; fixed some docstrings --- src/sage/combinat/designs/gen_quadrangles.pyx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/designs/gen_quadrangles.pyx b/src/sage/combinat/designs/gen_quadrangles.pyx index 3ccb504368d..f6a2b3370f3 100644 --- a/src/sage/combinat/designs/gen_quadrangles.pyx +++ b/src/sage/combinat/designs/gen_quadrangles.pyx @@ -13,20 +13,19 @@ EXAMPLES:: sage: GQ2 Incidence structure with 1105 points and 325 blocks sage: GQ3 = GQ.dual() - sage: set(GQ3._points) == set([x-1 for x in GQ2._points]) + sage: set(GQ3._points) == set(GQ2._points) True - sage: blocks2 = [set(map(lambda x: x-1, b)) for b in GQ2._blocks] - sage: all([set(b) in blocks2 for b in GQ3._blocks]) + sage: GQ2.is_isomorphic(GQ3) # long time True AUTHORS: -- YOUR NAME (2005-01-03): initial version +- Ivo Maffei (2020-07-26): initial version """ # **************************************************************************** -# Copyright (C) 2013 IVO MAFFEI +# Copyright (C) 2020 Ivo Maffei # # 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 @@ -132,7 +131,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): sage: is_GQ_with_spread(*t) Traceback (most recent call last): ... - TypeError: 'sage.libs.gap.element.GapElement_Integer' object is not iterable + TypeError: 'int' object is not iterable sage: t = dual_GQ_ovoid(*t) sage: is_GQ_with_spread(*t) True @@ -229,8 +228,9 @@ def dual_GQ_ovoid(GQ,O): def generalised_quadrangle_hermitian(const int q): r""" - Construct the generalised quadrangle `H(3,q^2)` with an ovoid - The GQ has order `(q^2,q)` + Construct the generalised quadrangle `H(3,q^2)` with an ovoid. + + The GQ has order `(q^2,q)`. INPUT: @@ -292,7 +292,9 @@ def generalised_quadrangle_hermitian(const int q): line = libgap.Set([pointInt[p] for p in lineAsPoints]) lines = libgap.Orbit(GUp, line, libgap.OnSets) # all isotropic lines - # lines degines the GQ H(3,q^2) + lines = [list(map(lambda x: int(x-1), b)) for b in lines] # convert to int + # lines defines the GQ H(3,q^2) + # to find an ovoid, we embed H(3,q^2) in H(4,q^2) # the embedding is (a,b,c,d) -> (a,b,0,c,d) @@ -318,7 +320,7 @@ def generalised_quadrangle_hermitian(const int q): xq = p[3]**q for p2 in points: if p2[1]*xq + p2[2] == zero: # p collinear to p2 - ovoid.append(libgap(pointInt[p2])) + ovoid.append(pointInt[p2] - 1) D = IncidenceStructure(lines) return (D, ovoid) From ee0e18f36993d4b6529f2c3509d786b3a0249b05 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 27 Jul 2020 10:52:06 +0200 Subject: [PATCH 078/379] added references --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/designs/__init__.py | 1 + src/sage/combinat/designs/gen_quadrangles.pyx | 16 ++++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index cd29a527a0a..1955b25833e 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -104,6 +104,7 @@ Comprehensive Module list sage/combinat/designs/difference_matrices sage/combinat/designs/evenly_distributed_sets sage/combinat/designs/ext_rep + sage/combinat/designs/gen_quadrangles sage/combinat/designs/incidence_structures sage/combinat/designs/latin_squares sage/combinat/designs/orthogonal_arrays diff --git a/src/sage/combinat/designs/__init__.py b/src/sage/combinat/designs/__init__.py index 0109e02b0ea..f8de34936f6 100644 --- a/src/sage/combinat/designs/__init__.py +++ b/src/sage/combinat/designs/__init__.py @@ -27,6 +27,7 @@ - :ref:`sage.combinat.designs.steiner_quadruple_systems` - :ref:`sage.combinat.designs.twographs` - :ref:`sage.combinat.designs.database` +- :ref:`sage.combinat.designs.gen_quadrangles` **Technical things** diff --git a/src/sage/combinat/designs/gen_quadrangles.pyx b/src/sage/combinat/designs/gen_quadrangles.pyx index f6a2b3370f3..f10d890c02a 100644 --- a/src/sage/combinat/designs/gen_quadrangles.pyx +++ b/src/sage/combinat/designs/gen_quadrangles.pyx @@ -17,6 +17,12 @@ EXAMPLES:: True sage: GQ2.is_isomorphic(GQ3) # long time True + +REFERENCES: + +- [PT2009]_ + +- [TP1994]_ AUTHORS: @@ -67,6 +73,11 @@ def generalised_quadrangle_with_spread(const int s, const int t, sage: designs.generalised_quadrangle_with_spread(0, 2, existence=True) False + REFERENCES: + + For more on generalised quadrangles and their spread see [PT2009]_ or + [TP1994]_. + TESTS:: sage: GQ, S = designs.generalised_quadrangle_with_spread(2, 4) @@ -252,6 +263,11 @@ def generalised_quadrangle_hermitian(const int q): sage: t[0].is_generalized_quadrangle() # long time True + REFERENCES: + + For more on `H(3,q^2)` and the construction implemented here see [PT2009]_ + or [TP1994]_. + TESTS:: sage: from sage.combinat.designs.gen_quadrangles import \ From 049110f121822625f40a74816d3fc89d3f67b595 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 27 Jul 2020 16:58:41 -0400 Subject: [PATCH 079/379] 29844: fixed involution map on type IV points --- .../schemes/berkovich/berkovich_cp_element.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 9ebe1add148..825bd1666f7 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -2336,6 +2336,10 @@ def involution_map(self): The involution map is the extension of the map ``z |-> 1/z`` on `P^1(\CC_p)` to Berkovich space. + If zero is contained in every disk approximating a type IV point, + then the image under the involution map is not defined. To avoid + this error, increase precision. + OUTPUT: A point of the same Berkovich space. EXAMPLES: @@ -2374,6 +2378,21 @@ def involution_map(self): sage: B(1/81, 1.5).involution_map() Type III point centered at (3^4 + O(3^24) : 1 + O(3^20)) of radius 0.000228623685413809 + + :: + + sage: B([1, 2], [3, 1]).involution_map() + Traceback (most call last): + ... + ValueError: precision of type IV is not high enough to define image + + :: + + sage: B([1/81, 10/81], [10, 9]).involution_map() + Type IV point of precision 2, approximated by disks centered at + [(3^4 + O(3^24) : 1 + O(3^20)), (3^4 + 2*3^6 + 2*3^7 + 2*3^10 + 2*3^11 + + 2*3^14 + 2*3^15 + 2*3^18 + 2*3^19 + 2*3^22 + 2*3^23 + O(3^24) : 1 + O(3^20))] + ... with radii [0.00152415790275873, 0.00137174211248285] ... """ infty = self.parent()((1,0)) zero = self.parent()(0) @@ -2400,13 +2419,14 @@ def involution_map(self): berk_point = self.parent()(self.center()[i], self.radius()[i]) zero_check = berk_point.gt(zero) if zero_check: - new_center = 0 - new_radius = 1/self.radius()[i] + continue else: new_center = 1/self.center()[i][0] new_radius = self.radius()[i]/(self._custom_abs(self.center()[i][0])**2) - new_center_lst.append(new_center) - new_radius_lst.append(new_radius) + new_center_lst.append(new_center) + new_radius_lst.append(new_radius) + if len(new_center_lst) == 0: + raise ValueError('precision of type IV is not high enough to define image') return self.parent()(new_center_lst, new_radius_lst) def contained_in_interval(self, start, end): From 7e86788950d02a32f0a7b884fe017bcc06f11060 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 27 Jul 2020 17:32:20 -0400 Subject: [PATCH 080/379] 29844: fixed affine type IV points, fixed tests --- .../schemes/berkovich/berkovich_cp_element.py | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 825bd1666f7..6802d37bdc2 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -1519,9 +1519,13 @@ def involution_map(self): The involution map is the extension of the map ``z |-> 1/z`` on `\CC_p` to Berkovich space. - For Affine Berkovich Space, not defined for the type I + For affine Berkovich Space, not defined for the type I point centered at 0. + If zero is contained in every disk approximating a type IV point, + then the image under the involution map is not defined. To avoid + this error, increase precision. + OUTPUT: A point of the same Berkovich space. EXAMPLES: @@ -1545,6 +1549,33 @@ def involution_map(self): sage: Q3.involution_map() Type II point centered at 3 + O(3^21) of radius 3^-3 + TESTS:: + + sage: B = Berkovich_Cp_Affine(3) + sage: B(0).involution_map() + Traceback (most recent call last): + ... + ValueError: involution map not deffined on affine type I point centered at 0 + + :: + + sage: B(1/81, 1.5).involution_map() + Type III point centered at 3^4 + O(3^24) of radius 0.000228623685413809 + + :: + + sage: Q1 = B([1, 2], [3, 1]) + sage: Q1.involution_map() + Traceback (most recent call last): + ... + ValueError: precision of type IV is not high enough to define image + + :: + + sage: B([1/81, 10/81], [10, 9]).involution_map() + Type IV point of precision 2, approximated by disks centered at [3^4 + O(3^24), + 3^4 + 2*3^6 + 2*3^7 + 2*3^10 + 2*3^11 + 2*3^14 + 2*3^15 + 2*3^18 + 2*3^19 + 2*3^22 + + 2*3^23 + O(3^24)] ... with radii [0.00152415790275873, 0.00137174211248285] ... """ if self.type_of_point() == 1: if self.center() == 0: @@ -1566,16 +1597,17 @@ def involution_map(self): new_center_lst = [] new_radius_lst = [] for i in range(len(self.center())): - berk_point = self.parent()(self.center()[i], radius[i]) + berk_point = self.parent()(self.center()[i], self.radius()[i]) zero_check = berk_point.gt(zero) if zero_check: - new_center = 0 - new_radius = RR(1/radius[i]) + continue else: new_center = 1/self.center()[i] - new_radius = RR(radius[i] / (self._custom_abs(self.center()[i])**2)) - new_center_lst.append(new_center) - new_radius_lst.append(new_radius) + new_radius = self.radius()[i]/(self._custom_abs(self.center()[i])**2) + new_center_lst.append(new_center) + new_radius_lst.append(new_radius) + if len(new_center_lst) == 0: + raise ValueError('precision of type IV is not high enough to define image') return self.parent()(new_center_lst, new_radius_lst, error_check=False) def contained_in_interval(self, start, end): @@ -2382,7 +2414,7 @@ def involution_map(self): :: sage: B([1, 2], [3, 1]).involution_map() - Traceback (most call last): + Traceback (most recent call last): ... ValueError: precision of type IV is not high enough to define image From b99e452a216cda039dabae4a466aa5851c193c00 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 28 Jul 2020 14:45:04 +1000 Subject: [PATCH 081/379] Implement universal commutative algebra of a Lie algebra. --- src/doc/en/reference/references/index.rst | 4 + ...ite_dimensional_lie_algebras_with_basis.py | 96 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4df7644a2eb..556d6cb5c3d 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -209,6 +209,10 @@ REFERENCES: Math. Z. 233 (2000), no. 3, 601–623. :mathscinet:`MR1750939` +.. [AM2020] \A. L. Agore and G. Militaru. + *A new invariant for finite dimensional Leibniz/Lie algebras*. + Preprint, :arxiv:`2006.00711` (2020). + .. [AMOZ2006] Asahiro, Y. and Miyano, E. and Ono, H. and Zenmyo, K., *Graph orientation algorithms to minimize the maximum outdegree*. Proceedings of the 12th Computing: The Australasian Theory 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 d4edc0bd3c0..9a8d8619f00 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -1428,6 +1428,102 @@ def morphism(self, on_generators, codomain=None, base_map=None, check=True): return LieAlgebraMorphism_from_generators(on_generators, domain=self, codomain=codomain, base_map=base_map, check=check) + @cached_method + def universal_polynomials(self): + r""" + Return the family of universal polynomials of ``self``. + + The *universal polynomials* of a Lie algebra `L` with + basis `\{e_i\}_{i \in I}` and structure coefficients + `[e_i, e_j] = \tau_{ij}^a e_a is given by + + .. MATH:: + + P_{aij} = \sum_{u \in I} \tau_{ij}^u X_{au} + - \sum_{s,t \in I} \tau_{st}^a X_{ai} X_{tj}, + + where `a,i,j \in I`. + + REFERENCES: + + - [AM2020]_ + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: L.universal_polynomials() + Finite family {('x', 'x', 'y'): X01*X10 - X00*X11 + X00, + ('y', 'x', 'y'): X10} + + sage: L = LieAlgebra(QQ, cartan_type=['A',1]) + sage: list(L.universal_polynomials()) + [-2*X01*X10 + 2*X00*X11 - 2*X00, + -2*X02*X10 + 2*X00*X12 + X01, + -2*X02*X11 + 2*X01*X12 - 2*X02, + X01*X20 - X00*X21 - 2*X10, + X02*X20 - X00*X22 + X11, + X02*X21 - X01*X22 - 2*X12, + -2*X11*X20 + 2*X10*X21 - 2*X20, + -2*X12*X20 + 2*X10*X22 + X21, + -2*X12*X21 + 2*X11*X22 - 2*X22] + """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + I = self.basis().keys() + n = len(I) + s_coeffs = self.structure_coefficients(True) + zero = self.base_ring().zero() + def sc(i, j): + if i == j: + return zero + if i > j: + return -s_coeffs[I[j],I[i]] + return s_coeffs[I[i],I[j]] + d = {} + keys = [] + R = PolynomialRing(self.base_ring(), ','.join('X{}{}'.format(i,j) + for i in range(n) + for j in range(n))) + X = [[R.gen(i+n*j) for i in range(n)] for j in range(n)] + for a in range(n): + for i in range(n): + for j in range(i+1, n): + k = (I[a], I[i], I[j]) + keys.append(k) + if i != j: + s = sc(i, j) + d[k] = (R.sum(s[I[u]] * X[a][u] for u in range(n)) + - R.sum(sc(s,t)[I[a]] * X[s][i] * X[t][j] + for s in range(n) for t in range(n) if s != t)) + else: + d[k] = -R.sum(sc(s,t)[I[a]] * X[s][i] * X[t][j] + for s in range(n) for t in range(n) if s != t) + return Family(keys, d.__getitem__) + + @cached_method + def universal_commutative_algebra(self): + r""" + Return the universal commutative algebra associated to ``self``. + + Let `I` be the index set of the basis of ``self``. Let + `\mathcal{P} = \{P_{a,i,j}\}_{a,i,j \in I}` denote the + universal polynomials of a Lie algebra `L`. The *universal + commutative algebra* associated to `L` is the quotient + ring `R[X_{ij}]_{i,j \in I} / (\mathcal{P})`. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: A = L.universal_commutative_algebra() + sage: a,b,c,d = A.gens() + sage: (a,b,c,d) + (X00bar, X01bar, 0, X11bar) + sage: a*d - a + 0 + """ + P = list(self.universal_polynomials()) + R = P[0].parent() + return R.quotient(P) + class ElementMethods: def adjoint_matrix(self): # In #11111 (more or less) by using matrix of a morphism """ From 8ae87450b2709a36a5b8a3b91ee4ef5437f55b31 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Wed, 29 Jul 2020 00:38:49 +0530 Subject: [PATCH 082/379] made list of lists as return type --- src/sage/graphs/base/boost_graph.pyx | 32 +++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 693481155b2..905e2352a5c 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -997,9 +997,9 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): pred = {} if weight_function is not None: - correct_type = type(weight_function(next(g.edge_iterator()))) + correct_type = type(weight_function(g.edges()[0]) elif g.weighted(): - correct_type = type(next(g.edge_iterator())[2]) + correct_type = type(next(g.edges()[0][2]) else: correct_type = int # Needed for rational curves. @@ -2197,6 +2197,7 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al ValueError: the starting vertex 55 is not in the graph """ import sys + from sage.rings.infinity import Infinity from sage.graphs.generic_graph import GenericGraph if not isinstance(g, GenericGraph): @@ -2257,8 +2258,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if correct_type == RealNumber: correct_type = RR - cdef dict distances = {} - cdef dict predecessors = {} + cdef list distances = [] + cdef list predecessors = [] for v in vertex_list: vi = v_to_int[v] @@ -2305,21 +2306,22 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al else: raise ValueError(f"unknown algorithm {algorithm!r}") - dist_v = {} - pred_v = {} + dist_v = [] + pred_v = [] for vert in range(g.num_verts()): if result.distances[vert] != sys.float_info.max: - w = int_to_v[vert] - dist_v[w] = correct_type(result.distances[vert]) + dist_v.append(correct_type(result.distances[vert])) pred = result.predecessors[vert] - pred_v[w] = int_to_v[pred] if pred != vert else None - - distances[v] = dist_v - predecessors[v] = pred_v + if pred != vert: + pred_v.append(pred) + else: + pred_v.append(None) + else: + dist_v.append(+Infinity) + pred_v.append(None) - if len(vertex_list) == 1: - v = vertex_list[0] - return distances[v], predecessors[v] + distances.append(dist_v) + predecessors.append(pred_v) return distances, predecessors From c309ab42d962c6ff7017aa4533c97d6f765a3236 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Wed, 29 Jul 2020 01:00:08 +0530 Subject: [PATCH 083/379] minor mistake --- src/sage/graphs/base/boost_graph.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 905e2352a5c..d495e0cd026 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -997,9 +997,9 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): pred = {} if weight_function is not None: - correct_type = type(weight_function(g.edges()[0]) + correct_type = type(weight_function(g.edges()[0])) elif g.weighted(): - correct_type = type(next(g.edges()[0][2]) + correct_type = type(g.edges()[0][2]) else: correct_type = int # Needed for rational curves. From 1683ff7225354e7ca054fab9ee942c7614ef3f94 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 8 Jul 2020 13:14:48 -0600 Subject: [PATCH 084/379] First draft --- src/sage/combinat/root_system/all.py | 1 + .../coxeter_fully_commutative_words.py | 134 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/sage/combinat/root_system/coxeter_fully_commutative_words.py diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index ccb40064690..63e7f41e7ad 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -11,6 +11,7 @@ from .coxeter_matrix import CoxeterMatrix from .coxeter_type import CoxeterType from .root_system import RootSystem, WeylDim +from .coxeter_fully_commutative_words import FullyCommutativeReducedCoxeterWords lazy_import('sage.combinat.root_system.weyl_group', ['WeylGroup', 'WeylGroupElement']) lazy_import('sage.combinat.root_system.reflection_group_real', diff --git a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py new file mode 100644 index 00000000000..83b381da154 --- /dev/null +++ b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py @@ -0,0 +1,134 @@ +from ..words.words import FiniteWords +from ..words.word import FiniteWord_list +from .coxeter_matrix import CoxeterMatrix +from .cartan_type import CartanType + +from sage.misc.lazy_attribute import lazy_attribute + +def FullyCommutativeReducedCoxeterWords(data): + if isinstance(data, CoxeterMatrix): + return FullyCommutativeReducedCoxeterWords_class(data) + else: + try: + t = CartanType(data) + except (TypeError, ValueError): + raise ValueError('Input must be a CoxeterMatrix or data for a Cartan type.') + return FullyCommutativeReducedCoxeterWords_class(t.coxeter_matrix()) + +class FullyCommutativeReducedCoxeterWords_class(FiniteWords): + def __init__(self, coxeter_matrix): + self._matrix = coxeter_matrix + FiniteWords.__init__(self, sorted(self._matrix.index_set())) + + @lazy_attribute + def _element_classes(self): + return {'list': FullyCommutativeReducedCoxeterWord_list} + + def coxeter_matrix(self): + return self._matrix + + def __iter__(self): + m = self.coxeter_matrix() + + empty_word = self._element_classes['list'](self, []) + letters = self.coxeter_matrix().index_set() + + recent_words = {empty_word} + yield empty_word + + length = 1 + while True: + new_words = set() + for w in recent_words: + for s in letters: + if w.still_reduced_fc_after_prepending(s): + sw = self._element_classes['list'](self, [s] + list(w)).cartier_foata() + new_words.add(sw) + + if len(new_words) == 0: + return + for w in new_words: + yield w + recent_words = new_words + length += 1 + + # Operations on or between letters and words are implemented on the parent, + # all taking letters as integers and words as lists. + # This is for performance reasons; methods on the elements are provided + # that delegate to these functions on the parent. + + def _find_left_descent(self, s, w): + m = self.coxeter_matrix() + for (i, t) in enumerate(w): + if t == s and not any(m[x, t] > 2 for x in w[:i]): + return i + return None + + def _left_descents(self, w): + m = self.coxeter_matrix() + out = set() + for (i, t) in enumerate(w): + if not any(m[x, t] > 2 for x in w[:i]): + out.add(t) + return out + + def _cartier_foata(self, w): + cur_word = list(w) + out_word = [] + + while len(cur_word) > 0: + for s in self.alphabet(): + i = self._find_left_descent(s, cur_word) + if i is not None: + out_word.append(s) + # cur_word = cur_word[:i] + cur_word[i+1:] + cur_word.pop(i) + + return self._element_classes['list'](self, out_word) + + def _still_reduced_fc_after_prepending(self, s, w): + m = self.coxeter_matrix() + if self._find_left_descent(s, w) is not None: + return False + + # Find the first letter in that doesn't commute with s. + try: + (j, t) = next((i, x) for (i, x) in enumerate(w) if m[s, x] >= 3) + except StopIteration: + return True + + u = w[j:] + for c in range(m[s, t] - 1): + letter = t if c % 2 == 0 else s + i = self._find_left_descent(letter, u) + if i is not None: + # Remove letter + # u = u[:i] + u[i+1:] + # u = u.with_index_removed(i) + u.pop(i) + else: + return True + + return False + +class FullyCommutativeReducedCoxeterWord_list(FiniteWord_list): + def _repr_(self): + return 'FC ' + super()._repr_() + + # Provide public methods on the elements that wrap the private element + # methods in the parent. + + def find_left_descent(self, s): + return self.parent()._find_left_descent(s, list(self)) + + def has_left_descent(self, s): + return self.find_left_descent(s) is not None + + def left_descents(self): + return self.parent()._left_descents(list(self)) + + def cartier_foata(self): + return self.parent()._cartier_foata(list(self)) + + def still_reduced_fc_after_prepending(self, s): + return self.parent()._still_reduced_fc_after_prepending(s, list(self)) From 4b7d6b044420ccb9dcd8fcbdbce44a2ff09a426e Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 8 Jul 2020 20:59:33 +0000 Subject: [PATCH 085/379] Demo using class with NormalizedClonableList --- .../coxeter_fully_commutative_words.py | 127 +++++++++++++----- 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py index 83b381da154..d7c7f4d8c3e 100644 --- a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py +++ b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py @@ -1,9 +1,15 @@ +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import NormalizedClonableList, ClonableArray +from sage.misc.classcall_metaclass import ClasscallMetaclass +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from ..words.words import FiniteWords from ..words.word import FiniteWord_list from .coxeter_matrix import CoxeterMatrix from .cartan_type import CartanType - -from sage.misc.lazy_attribute import lazy_attribute +from collections import deque +from sage.rings.all import Infinity def FullyCommutativeReducedCoxeterWords(data): if isinstance(data, CoxeterMatrix): @@ -15,14 +21,51 @@ def FullyCommutativeReducedCoxeterWords(data): raise ValueError('Input must be a CoxeterMatrix or data for a Cartan type.') return FullyCommutativeReducedCoxeterWords_class(t.coxeter_matrix()) -class FullyCommutativeReducedCoxeterWords_class(FiniteWords): +class FullyCommutativeReducedCoxeterWord(NormalizedClonableList): + def check(self): + if not self.parent()._is_fully_commutative(self._get_list()): + raise ValueError('list does not represent a fully commutative word.') + + def normalize(self): + self._require_mutable() + normalized = self.parent()._cartier_foata(self._get_list()) + self._set_list(normalized) + + # Provide public methods on the elements that wrap the private element + # methods in the parent. + + def find_left_descent(self, s): + return self.parent()._find_left_descent(s, list(self)) + + def has_left_descent(self, s): + return self.find_left_descent(s) is not None + + def left_descents(self): + return self.parent()._left_descents(list(self)) + + def still_reduced_fc_after_prepending(self, s): + return self.parent()._still_reduced_fc_after_prepending(s, list(self)) + + +class FullyCommutativeReducedCoxeterWords_class(Parent): def __init__(self, coxeter_matrix): self._matrix = coxeter_matrix - FiniteWords.__init__(self, sorted(self._matrix.index_set())) - @lazy_attribute - def _element_classes(self): - return {'list': FullyCommutativeReducedCoxeterWord_list} + category = InfiniteEnumeratedSets() + if self._matrix.is_finite(): + category = FiniteEnumeratedSets() + else: + cartan_type = self._matrix.coxeter_type().cartan_type() + family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() + if not affine and (rank == 2 or family in {'A', 'B', 'C', 'D', 'E', 'F', 'H'}): + category = FiniteEnumeratedSets() + + Parent.__init__(self, category=category) + + def _element_constructor_(self, lst): + return self.element_class(self, lst) + + Element = FullyCommutativeReducedCoxeterWord def coxeter_matrix(self): return self._matrix @@ -30,7 +73,7 @@ def coxeter_matrix(self): def __iter__(self): m = self.coxeter_matrix() - empty_word = self._element_classes['list'](self, []) + empty_word = self.element_class(self, [], check=False) letters = self.coxeter_matrix().index_set() recent_words = {empty_word} @@ -42,7 +85,7 @@ def __iter__(self): for w in recent_words: for s in letters: if w.still_reduced_fc_after_prepending(s): - sw = self._element_classes['list'](self, [s] + list(w)).cartier_foata() + sw = self.element_class(self, [s] + list(w), check=False) new_words.add(sw) if len(new_words) == 0: @@ -52,10 +95,42 @@ def __iter__(self): recent_words = new_words length += 1 - # Operations on or between letters and words are implemented on the parent, - # all taking letters as integers and words as lists. - # This is for performance reasons; methods on the elements are provided - # that delegate to these functions on the parent. + def _is_fully_commutative(self, w): + matrix = self.coxeter_matrix() + + def contains_long_braid(w): + for i in range(0, len(w)-2): + a = w[i] + b = w[i+1] + m = matrix[a, b] + if m > 2 and i+m <= len(w): + ab_braid = (a, b) * (m // 2) + ((a,) if m % 2 == 1 else ()) + if w[i:i+m] == ab_braid: + return True + return False + + def commute_once(word, i): + return word[:i] + (word[i+1], word[i]) + word[i+2:] + + w = tuple(w) + + if contains_long_braid(w): + return False + else: + l, checked, queue = len(w), {w}, deque([w]) + while queue: + word = queue.pop() + for i in range(l-1): + a, b = word[i], word[i+1] + if matrix[a, b] == 2: + new_word = commute_once(word, i) + if new_word not in checked: + if contains_long_braid(new_word): + return False + else: + checked.add(new_word) + queue.appendleft(new_word) + return True def _find_left_descent(self, s, w): m = self.coxeter_matrix() @@ -77,14 +152,14 @@ def _cartier_foata(self, w): out_word = [] while len(cur_word) > 0: - for s in self.alphabet(): + for s in self.coxeter_matrix().index_set(): i = self._find_left_descent(s, cur_word) if i is not None: out_word.append(s) # cur_word = cur_word[:i] + cur_word[i+1:] cur_word.pop(i) - return self._element_classes['list'](self, out_word) + return out_word def _still_reduced_fc_after_prepending(self, s, w): m = self.coxeter_matrix() @@ -110,25 +185,3 @@ def _still_reduced_fc_after_prepending(self, s, w): return True return False - -class FullyCommutativeReducedCoxeterWord_list(FiniteWord_list): - def _repr_(self): - return 'FC ' + super()._repr_() - - # Provide public methods on the elements that wrap the private element - # methods in the parent. - - def find_left_descent(self, s): - return self.parent()._find_left_descent(s, list(self)) - - def has_left_descent(self, s): - return self.find_left_descent(s) is not None - - def left_descents(self): - return self.parent()._left_descents(list(self)) - - def cartier_foata(self): - return self.parent()._cartier_foata(list(self)) - - def still_reduced_fc_after_prepending(self, s): - return self.parent()._still_reduced_fc_after_prepending(s, list(self)) From 9283e53c973690b66b4d046ac8e27f21eeb19972 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 8 Jul 2020 15:31:07 -0600 Subject: [PATCH 086/379] Restucture methods back onto element class --- .../coxeter_fully_commutative_words.py | 108 ++++++++---------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py index d7c7f4d8c3e..630f7f0a8d9 100644 --- a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py +++ b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py @@ -28,28 +28,67 @@ def check(self): def normalize(self): self._require_mutable() - normalized = self.parent()._cartier_foata(self._get_list()) - self._set_list(normalized) + + out_word = [] + + while len(self) > 0: + for s in self.parent().index_set(): + i = self.find_left_descent(s) + if i is not None: + out_word.append(s) + self.pop(i) + + self._set_list(out_word) # Provide public methods on the elements that wrap the private element # methods in the parent. def find_left_descent(self, s): - return self.parent()._find_left_descent(s, list(self)) + m = self.parent().coxeter_matrix() + for (i, t) in enumerate(self): + if t == s and not any(m[x, t] > 2 for x in self[:i]): + return i + return None def has_left_descent(self, s): return self.find_left_descent(s) is not None def left_descents(self): - return self.parent()._left_descents(list(self)) + m = self.parent().coxeter_matrix() + out = set() + for (i, t) in enumerate(self): + if not any(m[x, t] > 2 for x in self[:i]): + out.add(t) + return out def still_reduced_fc_after_prepending(self, s): - return self.parent()._still_reduced_fc_after_prepending(s, list(self)) + m = self.parent().coxeter_matrix() + if self.has_left_descent(s): + return False + + # Find the first letter in that doesn't commute with s. + try: + (j, t) = next((i, x) for (i, x) in enumerate(self) if m[s, x] >= 3) + except StopIteration: + return True + + u = self.clone() + u._set_list(self[j:]) + for c in range(m[s, t] - 1): + letter = t if c % 2 == 0 else s + i = u.find_left_descent(letter) + if i is not None: + u.pop(i) + else: + return True + + return False class FullyCommutativeReducedCoxeterWords_class(Parent): def __init__(self, coxeter_matrix): self._matrix = coxeter_matrix + self._index_set = sorted(coxeter_matrix.index_set()) category = InfiniteEnumeratedSets() if self._matrix.is_finite(): @@ -70,11 +109,14 @@ def _element_constructor_(self, lst): def coxeter_matrix(self): return self._matrix + def index_set(self): + return self._index_set + def __iter__(self): m = self.coxeter_matrix() empty_word = self.element_class(self, [], check=False) - letters = self.coxeter_matrix().index_set() + letters = self.index_set() recent_words = {empty_word} yield empty_word @@ -131,57 +173,3 @@ def commute_once(word, i): checked.add(new_word) queue.appendleft(new_word) return True - - def _find_left_descent(self, s, w): - m = self.coxeter_matrix() - for (i, t) in enumerate(w): - if t == s and not any(m[x, t] > 2 for x in w[:i]): - return i - return None - - def _left_descents(self, w): - m = self.coxeter_matrix() - out = set() - for (i, t) in enumerate(w): - if not any(m[x, t] > 2 for x in w[:i]): - out.add(t) - return out - - def _cartier_foata(self, w): - cur_word = list(w) - out_word = [] - - while len(cur_word) > 0: - for s in self.coxeter_matrix().index_set(): - i = self._find_left_descent(s, cur_word) - if i is not None: - out_word.append(s) - # cur_word = cur_word[:i] + cur_word[i+1:] - cur_word.pop(i) - - return out_word - - def _still_reduced_fc_after_prepending(self, s, w): - m = self.coxeter_matrix() - if self._find_left_descent(s, w) is not None: - return False - - # Find the first letter in that doesn't commute with s. - try: - (j, t) = next((i, x) for (i, x) in enumerate(w) if m[s, x] >= 3) - except StopIteration: - return True - - u = w[j:] - for c in range(m[s, t] - 1): - letter = t if c % 2 == 0 else s - i = self._find_left_descent(letter, u) - if i is not None: - # Remove letter - # u = u[:i] + u[i+1:] - # u = u.with_index_removed(i) - u.pop(i) - else: - return True - - return False From 43f3d9a74524e1cc704362fc5176154f4e928c35 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 00:23:07 -0600 Subject: [PATCH 087/379] Enhancements and docs --- src/sage/combinat/root_system/all.py | 2 +- .../coxeter_fully_commutative_words.py | 175 --------- .../fully_commutative_coxeter_elements.py | 336 ++++++++++++++++++ 3 files changed, 337 insertions(+), 176 deletions(-) delete mode 100644 src/sage/combinat/root_system/coxeter_fully_commutative_words.py create mode 100644 src/sage/combinat/root_system/fully_commutative_coxeter_elements.py diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index 63e7f41e7ad..9c3825734de 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -11,7 +11,7 @@ from .coxeter_matrix import CoxeterMatrix from .coxeter_type import CoxeterType from .root_system import RootSystem, WeylDim -from .coxeter_fully_commutative_words import FullyCommutativeReducedCoxeterWords +from .fully_commutative_coxeter_elements import FullyCommutativeCoxeterElements lazy_import('sage.combinat.root_system.weyl_group', ['WeylGroup', 'WeylGroupElement']) lazy_import('sage.combinat.root_system.reflection_group_real', diff --git a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py b/src/sage/combinat/root_system/coxeter_fully_commutative_words.py deleted file mode 100644 index 630f7f0a8d9..00000000000 --- a/src/sage/combinat/root_system/coxeter_fully_commutative_words.py +++ /dev/null @@ -1,175 +0,0 @@ -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.list_clone import NormalizedClonableList, ClonableArray -from sage.misc.classcall_metaclass import ClasscallMetaclass -from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from ..words.words import FiniteWords -from ..words.word import FiniteWord_list -from .coxeter_matrix import CoxeterMatrix -from .cartan_type import CartanType -from collections import deque -from sage.rings.all import Infinity - -def FullyCommutativeReducedCoxeterWords(data): - if isinstance(data, CoxeterMatrix): - return FullyCommutativeReducedCoxeterWords_class(data) - else: - try: - t = CartanType(data) - except (TypeError, ValueError): - raise ValueError('Input must be a CoxeterMatrix or data for a Cartan type.') - return FullyCommutativeReducedCoxeterWords_class(t.coxeter_matrix()) - -class FullyCommutativeReducedCoxeterWord(NormalizedClonableList): - def check(self): - if not self.parent()._is_fully_commutative(self._get_list()): - raise ValueError('list does not represent a fully commutative word.') - - def normalize(self): - self._require_mutable() - - out_word = [] - - while len(self) > 0: - for s in self.parent().index_set(): - i = self.find_left_descent(s) - if i is not None: - out_word.append(s) - self.pop(i) - - self._set_list(out_word) - - # Provide public methods on the elements that wrap the private element - # methods in the parent. - - def find_left_descent(self, s): - m = self.parent().coxeter_matrix() - for (i, t) in enumerate(self): - if t == s and not any(m[x, t] > 2 for x in self[:i]): - return i - return None - - def has_left_descent(self, s): - return self.find_left_descent(s) is not None - - def left_descents(self): - m = self.parent().coxeter_matrix() - out = set() - for (i, t) in enumerate(self): - if not any(m[x, t] > 2 for x in self[:i]): - out.add(t) - return out - - def still_reduced_fc_after_prepending(self, s): - m = self.parent().coxeter_matrix() - if self.has_left_descent(s): - return False - - # Find the first letter in that doesn't commute with s. - try: - (j, t) = next((i, x) for (i, x) in enumerate(self) if m[s, x] >= 3) - except StopIteration: - return True - - u = self.clone() - u._set_list(self[j:]) - for c in range(m[s, t] - 1): - letter = t if c % 2 == 0 else s - i = u.find_left_descent(letter) - if i is not None: - u.pop(i) - else: - return True - - return False - - -class FullyCommutativeReducedCoxeterWords_class(Parent): - def __init__(self, coxeter_matrix): - self._matrix = coxeter_matrix - self._index_set = sorted(coxeter_matrix.index_set()) - - category = InfiniteEnumeratedSets() - if self._matrix.is_finite(): - category = FiniteEnumeratedSets() - else: - cartan_type = self._matrix.coxeter_type().cartan_type() - family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() - if not affine and (rank == 2 or family in {'A', 'B', 'C', 'D', 'E', 'F', 'H'}): - category = FiniteEnumeratedSets() - - Parent.__init__(self, category=category) - - def _element_constructor_(self, lst): - return self.element_class(self, lst) - - Element = FullyCommutativeReducedCoxeterWord - - def coxeter_matrix(self): - return self._matrix - - def index_set(self): - return self._index_set - - def __iter__(self): - m = self.coxeter_matrix() - - empty_word = self.element_class(self, [], check=False) - letters = self.index_set() - - recent_words = {empty_word} - yield empty_word - - length = 1 - while True: - new_words = set() - for w in recent_words: - for s in letters: - if w.still_reduced_fc_after_prepending(s): - sw = self.element_class(self, [s] + list(w), check=False) - new_words.add(sw) - - if len(new_words) == 0: - return - for w in new_words: - yield w - recent_words = new_words - length += 1 - - def _is_fully_commutative(self, w): - matrix = self.coxeter_matrix() - - def contains_long_braid(w): - for i in range(0, len(w)-2): - a = w[i] - b = w[i+1] - m = matrix[a, b] - if m > 2 and i+m <= len(w): - ab_braid = (a, b) * (m // 2) + ((a,) if m % 2 == 1 else ()) - if w[i:i+m] == ab_braid: - return True - return False - - def commute_once(word, i): - return word[:i] + (word[i+1], word[i]) + word[i+2:] - - w = tuple(w) - - if contains_long_braid(w): - return False - else: - l, checked, queue = len(w), {w}, deque([w]) - while queue: - word = queue.pop() - for i in range(l-1): - a, b = word[i], word[i+1] - if matrix[a, b] == 2: - new_word = commute_once(word, i) - if new_word not in checked: - if contains_long_braid(new_word): - return False - else: - checked.add(new_word) - queue.appendleft(new_word) - return True diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py new file mode 100644 index 00000000000..63733477d54 --- /dev/null +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -0,0 +1,336 @@ +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import NormalizedClonableList, ClonableArray +from sage.misc.classcall_metaclass import ClasscallMetaclass +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from ..words.words import FiniteWords +from ..words.word import FiniteWord_list +from .coxeter_matrix import CoxeterMatrix +from .cartan_type import CartanType +from collections import deque +from sage.rings.all import Infinity + +def FullyCommutativeCoxeterElements(data): + r""" + The combinatorial class of reduced words of fully commutative elements in a + Coxeter group. + + Given a Coxeter system with Coxeter matrix `m`, a *fully commutative* + element `w` is an element with the property that any reduced word for `w` + can be transformed into any other reduced word for `w` by applying + commutation relations only. Equivalently, `w` is an element such that no + reduced word for `w` contains a long braid. + + Fully commutative elements of a Coxeter system have a unique canonical form + called the *Cartier--Foata normal form*. All elements of this class are + automatically normalized to Cartier--Foata form, and equality of elements is + decied by the equality of their normal forms. + + Certain Coxeter systems are *FC-finite*, meaning they contain finitely many + fully commutative elements. The seven FC-finite Coxeter group families are + (non-affine) `A_n`, `B_n = C_n`, `D_n`, `E_n`, `F_n`, `H_n` and `I_2(m)`. + + INPUT: + + - ``data`` -- either data describing a Cartan type, or a CoxeterMatrix. Same + datum as the :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup` + constructor. + + OUTPUT: + + The class of reduced words of fully commutative elements in the Coxeter + group described by ``data``. This will belong to either the category of + infinite enumerated sets or finite enumerated sets depending on if the group + is FC-finite. + + EXAMPLES: + + Enumerate the reduced words of fully commutative elements in `A_3` :: + + sage: FC = FullyCommutativeReducedCoxeterWords(['A', 3]) + sage: FC.category() + Category of finite enumerated sets + sage: FC.list() + [[], + [2], + [3], + [1], + [2, 3], + [1, 2], + [3, 2], + [1, 3], + [2, 1], + [1, 2, 3], + [3, 2, 1], + [1, 3, 2], + [2, 3, 1], + [2, 3, 1, 2]] + + Count the FC elements in `B_8` :: + + sage: FC = FullyCommutativeCoxeterElements(['B', 8]) + sage: len(FC) # long time (7 seconds) + 14299 + + Iterate through the fully commutative elements of length up to 3 in the non + FC-finite group affine `A_2` :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 2, 1]) + sage: FC.category() + Category of infinite enumerated sets + sage: list(FC.iterate_to_length(2)) + [[], [1], [2], [0], [1, 0], [0, 2], [0, 1], [1, 2], [2, 1], [2, 0]] + + Constructing an element that is not fully commutative throws an error :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC([1,2,1]) + Traceback (most recent call last): + ... + ValueError: list does not represent a fully commutative word. + + Elements are normalized to Cartier--Foata normal form upon construction :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC([2, 1, 3, 2]) + [2, 3, 1, 2] + + """ + if isinstance(data, CoxeterMatrix): + return FullyCommutativeCoxeterElements_class(data) + else: + try: + t = CartanType(data) + except (TypeError, ValueError): + raise ValueError('Input must be a CoxeterMatrix or data for a Cartan type.') + return FullyCommutativeCoxeterElements_class(t.coxeter_matrix()) + +class FullyCommutativeCoxeterElement(NormalizedClonableList): + r""" + A (reduced word of a) fully commutative element in the Coxeter system. + """ + + # Methods required as a subclass of NormalizedClonableList: + def check(self): + r""" + Determine if the word ``self`` actually represents a fully commutative + reduced word. Raises a ValueError if this is not the case. + """ + if not self.parent()._is_fully_commutative(self._get_list()): + raise ValueError('list does not represent a fully commutative word.') + + def normalize(self): + r""" + Normalize ``self`` to Cartier--Foata normal form. + """ + self._require_mutable() + + out_word = [] + + while len(self) > 0: + for s in self.parent().index_set(): + i = self.find_left_descent(s) + if i is not None: + out_word.append(s) + self.pop(i) + + self._set_list(out_word) + + # Operations on fully commutative elements: + + def find_left_descent(self, s): + r""" + Determine if ``s`` is a left descent of ``self``, and find the index of + its occurrence in ``self``. + + INPUT: + + - ``s`` -- integer; the index of the generator (element of + ``self.parent().index_set()``). + + OUTPUT: + + The index of ``s`` in ``self`` if ``s`` is a left-descent, + ``None`` if ``s`` is not a left descent. + """ + m = self.parent().coxeter_matrix() + for (i, t) in enumerate(self): + if t == s and not any(m[x, t] > 2 for x in self[:i]): + return i + return None + + def has_left_descent(self, s): + r""" + Determine if ``s`` is a left descent of ``self``. + """ + return self.find_left_descent(s) is not None + + def left_descents(self): + r""" + Obtain the set of left descents of ``self``. + + OUTPUT: + + A set of integers representing the indices of generators which are left + descents of ``self``. + """ + m = self.parent().coxeter_matrix() + out = set() + for (i, t) in enumerate(self): + if not any(m[x, t] > 2 for x in self[:i]): + out.add(t) + return out + + def still_reduced_fc_after_prepending(self, s): + r""" + Determine if ``self`` prepended with ``s`` is still a reduced word of a + fully commutative element in the Coxeter system. + + INPUT: + + - ``s`` -- integer; the index of the generator (element of + ``self.parent().index_set()``). + """ + m = self.parent().coxeter_matrix() + if self.has_left_descent(s): + return False + + # Find the first letter in that doesn't commute with s. + try: + (j, t) = next((i, x) for (i, x) in enumerate(self) if m[s, x] >= 3) + except StopIteration: + return True + + u = self.clone() + u._set_list(self[j:]) + for c in range(m[s, t] - 1): + letter = t if c % 2 == 0 else s + i = u.find_left_descent(letter) + if i is not None: + u.pop(i) + else: + return True + + return False + + +class FullyCommutativeCoxeterElements_class(Parent): + def __init__(self, coxeter_matrix): + self._matrix = coxeter_matrix + self._index_set = sorted(coxeter_matrix.index_set()) + + # Determine if this group is FC-finite. + category = InfiniteEnumeratedSets() + if self._matrix.is_finite(): + category = FiniteEnumeratedSets() + else: + cartan_type = self._matrix.coxeter_type().cartan_type() + family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() + if not affine and (rank == 2 or family in {'A', 'B', 'C', 'D', 'E', 'F', 'H'}): + category = FiniteEnumeratedSets() + + Parent.__init__(self, category=category) + + def _element_constructor_(self, lst): + return self.element_class(self, lst) + + Element = FullyCommutativeCoxeterElement + + def coxeter_matrix(self): + r""" + Obtain the Coxeter matrix of the associated Coxter system. + + OUTPUT: CoxeterMatrix + """ + return self._matrix + + def index_set(self): + r""" + Obtain the index set of the generators / simple reflections of the + associated Coxeter system. + + OUTPUT: iterable of integers + """ + return self._index_set + + def __iter__(self): + r""" + Enumerate the elements of this set by length, starting with the empty + word and, if the group is FC-finite, ending with the longest fully + commutative element. + """ + m = self.coxeter_matrix() + + empty_word = self.element_class(self, [], check=False) + letters = self.index_set() + + recent_words = {empty_word} + yield empty_word + + length = 1 + while True: + new_words = set() + for w in recent_words: + for s in letters: + if w.still_reduced_fc_after_prepending(s): + sw = self.element_class(self, [s] + list(w), check=False) + new_words.add(sw) + + if len(new_words) == 0: + return + for w in new_words: + yield w + recent_words = new_words + length += 1 + + def iterate_to_length(self, length): + r""" + Iterate through the elements of this class up to a maximum length. + + INPUT: + + - ``length`` -- integer; maximum length of element to generate. + """ + assert length >= 0 + for w in self: + if len(w) > length: + break + yield w + + def _is_fully_commutative(self, w): + matrix = self.coxeter_matrix() + + def contains_long_braid(w): + for i in range(0, len(w)-2): + a = w[i] + b = w[i+1] + m = matrix[a, b] + if m > 2 and i+m <= len(w): + ab_braid = (a, b) * (m // 2) + ((a,) if m % 2 == 1 else ()) + if w[i:i+m] == ab_braid: + return True + return False + + def commute_once(word, i): + return word[:i] + (word[i+1], word[i]) + word[i+2:] + + w = tuple(w) + + if contains_long_braid(w): + return False + else: + l, checked, queue = len(w), {w}, deque([w]) + while queue: + word = queue.pop() + for i in range(l-1): + a, b = word[i], word[i+1] + if matrix[a, b] == 2: + new_word = commute_once(word, i) + if new_word not in checked: + if contains_long_braid(new_word): + return False + else: + checked.add(new_word) + queue.appendleft(new_word) + return True From bd91c224898f279daeae7a9c97332eb0d4ab2474 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 01:23:54 -0600 Subject: [PATCH 088/379] Additions; docs --- .../fully_commutative_coxeter_elements.py | 125 +++++++++++++++++- 1 file changed, 119 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py index 63733477d54..4559eaaf1e9 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -1,15 +1,13 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.list_clone import NormalizedClonableList, ClonableArray -from sage.misc.classcall_metaclass import ClasscallMetaclass +from sage.structure.list_clone import NormalizedClonableList from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from ..words.words import FiniteWords -from ..words.word import FiniteWord_list +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from .coxeter_matrix import CoxeterMatrix from .cartan_type import CartanType from collections import deque -from sage.rings.all import Infinity +from sage.combinat.posets.posets import Poset +import itertools def FullyCommutativeCoxeterElements(data): r""" @@ -214,6 +212,121 @@ def still_reduced_fc_after_prepending(self, s): return False + def heap(self, **kargs): + r""" + Create the heap of ``self``. + + The heap of a fully commutative word `w` is a partial order on the + indices of a reduced word for `w`. + + OPTIONAL ARGUMENTS: + + - ``one_index`` -- boolean (default False); make the Poset on the + indices {1, 2, ..., n} instead of {0, 1, ..., n-1}. + + - ``display_labeling`` -- boolean (default False); make the elements of + the resulting Poset be tuples `(i, w_i)` where `w_i` is the letter at + index `i` in ``self``. + + .. NOTE: + + Fully commutative elements have the property that the heap of any + reduced word is unique up to Poset isomorphism. Of course, the + particular one returned from this method is created from the + Cartier--Foata normal form of the reduced word. + + OUTPUT: Poset; the partially ordered set on the indices of ``self``. + + EXAMPLES: + + Create the heap of a fully commutative element in `A_5` :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 5]) + sage: FC([1, 4, 3, 5, 4, 2]).heap().cover_relations() + [[1, 2], [1, 3], [2, 4], [3, 4], [3, 5], [0, 5]] + sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() + [[2, 3], [2, 4], [3, 5], [4, 5], [4, 6], [1, 6]] + """ + m = self.parent().coxeter_matrix() + + one_index = kargs.get('one_index', False) + display_labeling = kargs.get('display_labeling', False) + + # Elements are the indices {1, 2, ..., n} + elements = list(range(1, len(self) + 1)) if one_index else list(range(len(self))) + + # Get the letter of w at a certain index, respecting the indexing convention + def letter(index): + return self[index-1] if one_index else self[index] + + # The partial order is generated by the relations i \prec j for all i < j with m(w_i, w_j) != 2 + relations = [(i, j) for [i, j] in itertools.product(elements, repeat=2) if i < j and m[letter(i), letter(j)] != 2] + + # Create the poset from the transitive closure of these relations + p = Poset((elements, relations)) + if not display_labeling: + return p + else: + return p.relabel(lambda i: (i, letter(i))) + + def n_value(self): + r""" + Calculate the n-value of ``self``. + + The *n-value* of a fully commutative element is the *width* (length + of the longest antichain) of its heap. + """ + return self.heap().width() + + def plot(self): + r""" + Display a semantically helpful rendering of the heap of ``self``. + + Specifically, if ``self`` is the word `w`, render the Hasse diagram of + the heap of `w` where each vertex is labeled by `w_i` instead of `i`. + Furthermore, vertices are arranged in level sets, where the "level" of + an element is the maximum length of a chain ending in that element. + + OUTPUT: GraphicsObject + """ + import sage.plot.all as plot + from sage.modules.free_module_element import vector + + m = self.parent().coxeter_matrix() + letters = self.parent().index_set() + graphics = [] + + # Returns a line between two points that respects the space of the "nodes" + def short_line_between(a, b): + a, b = vector(a), vector(b) + d = (b - a).normalized() + a2 = a + 0.1*d + b2 = b - 0.1*d + return plot.line([a2, b2], color='black') + + h = self.heap() + levels = h.level_sets() + letters_at_level = [set(self[i] for i in level) for level in levels] + + for (level_zero_index, members) in enumerate(levels): + level = level_zero_index + 1 + for i in members: + x = self[i] + + # Draw the node + graphics.append(plot.circle((x, level), 0.1)) + graphics.append(plot.text(str(x), (x, level))) + + neighbors = {z for z in letters if m[x, z] >= 3} + for other in neighbors: + highest_level = max((j + 1 for j in range(level_zero_index) if other in letters_at_level[j]), default=None) + if highest_level: + graphics.append(short_line_between((other, highest_level), (x, level))) + + g = sum(graphics) + g.axes(False) + return g + class FullyCommutativeCoxeterElements_class(Parent): def __init__(self, coxeter_matrix): From edbdf47416e33512286491332b9acc89908a9feb Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 03:15:48 -0600 Subject: [PATCH 089/379] Remove unnecessary factory function; fix documentation. --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/root_system/__init__.py | 1 + .../fully_commutative_coxeter_elements.py | 199 +++++++++--------- 3 files changed, 102 insertions(+), 99 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index cd29a527a0a..7c64f93c3dd 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -239,6 +239,7 @@ Comprehensive Module list sage/combinat/root_system/coxeter_group sage/combinat/root_system/coxeter_matrix sage/combinat/root_system/coxeter_type + sage/combinat/root_system/fully_commutative_coxeter_elements sage/combinat/root_system/dynkin_diagram sage/combinat/root_system/hecke_algebra_representation sage/combinat/root_system/integrable_representations diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index b04c503fad1..520692903bf 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -58,6 +58,7 @@ - :ref:`sage.combinat.root_system.fundamental_group` - :ref:`sage.combinat.root_system.braid_move_calculator` - :ref:`sage.combinat.root_system.braid_orbit` +- :ref:`sage.combinat.root_system.fully_commutative_coxeter_elements` .. SEEALSO:: diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py index 4559eaaf1e9..c23dfca0970 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -1,3 +1,6 @@ +r""" +Fully commutative Coxeter group elements +""" from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import NormalizedClonableList @@ -9,101 +12,6 @@ from sage.combinat.posets.posets import Poset import itertools -def FullyCommutativeCoxeterElements(data): - r""" - The combinatorial class of reduced words of fully commutative elements in a - Coxeter group. - - Given a Coxeter system with Coxeter matrix `m`, a *fully commutative* - element `w` is an element with the property that any reduced word for `w` - can be transformed into any other reduced word for `w` by applying - commutation relations only. Equivalently, `w` is an element such that no - reduced word for `w` contains a long braid. - - Fully commutative elements of a Coxeter system have a unique canonical form - called the *Cartier--Foata normal form*. All elements of this class are - automatically normalized to Cartier--Foata form, and equality of elements is - decied by the equality of their normal forms. - - Certain Coxeter systems are *FC-finite*, meaning they contain finitely many - fully commutative elements. The seven FC-finite Coxeter group families are - (non-affine) `A_n`, `B_n = C_n`, `D_n`, `E_n`, `F_n`, `H_n` and `I_2(m)`. - - INPUT: - - - ``data`` -- either data describing a Cartan type, or a CoxeterMatrix. Same - datum as the :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup` - constructor. - - OUTPUT: - - The class of reduced words of fully commutative elements in the Coxeter - group described by ``data``. This will belong to either the category of - infinite enumerated sets or finite enumerated sets depending on if the group - is FC-finite. - - EXAMPLES: - - Enumerate the reduced words of fully commutative elements in `A_3` :: - - sage: FC = FullyCommutativeReducedCoxeterWords(['A', 3]) - sage: FC.category() - Category of finite enumerated sets - sage: FC.list() - [[], - [2], - [3], - [1], - [2, 3], - [1, 2], - [3, 2], - [1, 3], - [2, 1], - [1, 2, 3], - [3, 2, 1], - [1, 3, 2], - [2, 3, 1], - [2, 3, 1, 2]] - - Count the FC elements in `B_8` :: - - sage: FC = FullyCommutativeCoxeterElements(['B', 8]) - sage: len(FC) # long time (7 seconds) - 14299 - - Iterate through the fully commutative elements of length up to 3 in the non - FC-finite group affine `A_2` :: - - sage: FC = FullyCommutativeCoxeterElements(['A', 2, 1]) - sage: FC.category() - Category of infinite enumerated sets - sage: list(FC.iterate_to_length(2)) - [[], [1], [2], [0], [1, 0], [0, 2], [0, 1], [1, 2], [2, 1], [2, 0]] - - Constructing an element that is not fully commutative throws an error :: - - sage: FC = FullyCommutativeCoxeterElements(['A', 3]) - sage: FC([1,2,1]) - Traceback (most recent call last): - ... - ValueError: list does not represent a fully commutative word. - - Elements are normalized to Cartier--Foata normal form upon construction :: - - sage: FC = FullyCommutativeCoxeterElements(['A', 3]) - sage: FC([2, 1, 3, 2]) - [2, 3, 1, 2] - - """ - if isinstance(data, CoxeterMatrix): - return FullyCommutativeCoxeterElements_class(data) - else: - try: - t = CartanType(data) - except (TypeError, ValueError): - raise ValueError('Input must be a CoxeterMatrix or data for a Cartan type.') - return FullyCommutativeCoxeterElements_class(t.coxeter_matrix()) - class FullyCommutativeCoxeterElement(NormalizedClonableList): r""" A (reduced word of a) fully commutative element in the Coxeter system. @@ -328,10 +236,103 @@ def short_line_between(a, b): return g -class FullyCommutativeCoxeterElements_class(Parent): - def __init__(self, coxeter_matrix): - self._matrix = coxeter_matrix - self._index_set = sorted(coxeter_matrix.index_set()) +class FullyCommutativeCoxeterElements(Parent): + r""" + The combinatorial class of reduced words of fully commutative elements in a + Coxeter group. + + Given a Coxeter system with Coxeter matrix `m`, a *fully commutative* + element `w` is an element with the property that any reduced word for `w` + can be transformed into any other reduced word for `w` by applying + commutation relations only. Equivalently, `w` is an element such that no + reduced word for `w` contains a long braid. + + Fully commutative elements of a Coxeter system have a unique canonical form + called the *Cartier--Foata normal form*. All elements of this class are + automatically normalized to Cartier--Foata form, and equality of elements is + decied by the equality of their normal forms. + + Certain Coxeter systems are *FC-finite*, meaning they contain finitely many + fully commutative elements. The seven FC-finite Coxeter group families are + (non-affine) `A_n`, `B_n = C_n`, `D_n`, `E_n`, `F_n`, `H_n` and `I_2(m)`. + + INPUT: + + - ``data`` -- either data describing a Cartan type, or a CoxeterMatrix. Same + datum as the :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup` + constructor. + + OUTPUT: + + The class of reduced words of fully commutative elements in the Coxeter + group described by ``data``. This will belong to either the category of + infinite enumerated sets or finite enumerated sets depending on if the group + is FC-finite. + + EXAMPLES: + + Enumerate the reduced words of fully commutative elements in `A_3` :: + + sage: FC = FullyCommutativeReducedCoxeterWords(['A', 3]) + sage: FC.category() + Category of finite enumerated sets + sage: FC.list() + [[], + [2], + [3], + [1], + [2, 3], + [1, 2], + [3, 2], + [1, 3], + [2, 1], + [1, 2, 3], + [3, 2, 1], + [1, 3, 2], + [2, 3, 1], + [2, 3, 1, 2]] + + Count the FC elements in `B_8` :: + + sage: FC = FullyCommutativeCoxeterElements(['B', 8]) + sage: len(FC) # long time (7 seconds) + 14299 + + Iterate through the fully commutative elements of length up to 3 in the non + FC-finite group affine `A_2` :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 2, 1]) + sage: FC.category() + Category of infinite enumerated sets + sage: list(FC.iterate_to_length(2)) + [[], [1], [2], [0], [1, 0], [0, 2], [0, 1], [1, 2], [2, 1], [2, 0]] + + Constructing an element that is not fully commutative throws an error :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC([1,2,1]) + Traceback (most recent call last): + ... + ValueError: list does not represent a fully commutative word. + + Elements are normalized to Cartier--Foata normal form upon construction :: + + sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC([2, 1, 3, 2]) + [2, 3, 1, 2] + + """ + def __init__(self, data): + if isinstance(data, CoxeterMatrix): + self._matrix = data + else: + try: + t = CartanType(data) + except (TypeError, ValueError): + raise ValueError('Input must be a CoxeterMatrix or data describing a Cartan type.') + self._matrix = t.coxeter_matrix() + + self._index_set = sorted(self._matrix.index_set()) # Determine if this group is FC-finite. category = InfiniteEnumeratedSets() From a2bc44790b6c81d8a6340e4544886e9e9dff7f5d Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 04:00:41 -0600 Subject: [PATCH 090/379] Fix cartier_foata and examples --- .../fully_commutative_coxeter_elements.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py index c23dfca0970..6d6900c7e29 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -29,17 +29,30 @@ def check(self): def normalize(self): r""" Normalize ``self`` to Cartier--Foata normal form. + + EXAMPLES: + + Observe the normal form of three equivalent FC words in `B_5` :: + + sage: FC = FullyCommutativeCoxeterElements(['B', 5]) + sage: FC([1, 4, 3, 5, 2, 4, 3]) + [1, 4, 3, 5, 2, 4, 3] + sage: FC([4, 1, 3, 5, 2, 4, 3]) + [1, 4, 3, 5, 2, 4, 3] + sage: FC([4, 1, 5, 3, 4, 2, 3]) + [1, 4, 3, 5, 2, 4, 3] + sage: FC([1, 4, 3, 5, 2, 4, 3]) == FC([4, 1, 3, 5, 2, 4, 3]) == FC([4, 1, 5, 3, 4, 2, 3]) + True """ self._require_mutable() out_word = [] while len(self) > 0: - for s in self.parent().index_set(): - i = self.find_left_descent(s) - if i is not None: - out_word.append(s) - self.pop(i) + fronts = self.left_descents() + out_word.extend(sorted(fronts)) + for s in fronts: + self.remove(s) self._set_list(out_word) From c7a433dbe89d95998ce63ba1fcaf4ea6929fdd2b Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 04:01:59 -0600 Subject: [PATCH 091/379] Fix incorrect examples/tests from broken cartier_foata --- .../root_system/fully_commutative_coxeter_elements.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py index 6d6900c7e29..3060623c70d 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -163,10 +163,10 @@ def heap(self, **kargs): Create the heap of a fully commutative element in `A_5` :: sage: FC = FullyCommutativeCoxeterElements(['A', 5]) - sage: FC([1, 4, 3, 5, 4, 2]).heap().cover_relations() - [[1, 2], [1, 3], [2, 4], [3, 4], [3, 5], [0, 5]] + sage: FC([1, 4, 3, 5, 2, 4]).heap().cover_relations() + [[1, 2], [1, 3], [2, 5], [2, 4], [3, 5], [0, 4]] sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() - [[2, 3], [2, 4], [3, 5], [4, 5], [4, 6], [1, 6]] + [[2, 3], [2, 4], [3, 6], [3, 5], [4, 6], [1, 5]] """ m = self.parent().coxeter_matrix() @@ -331,8 +331,8 @@ class FullyCommutativeCoxeterElements(Parent): Elements are normalized to Cartier--Foata normal form upon construction :: sage: FC = FullyCommutativeCoxeterElements(['A', 3]) - sage: FC([2, 1, 3, 2]) - [2, 3, 1, 2] + sage: FC([2, 3, 1, 2]) + [2, 1, 3, 2] """ def __init__(self, data): From 1056b77a57f6c8d91a15df688344108fa6d45f51 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 04:02:26 -0600 Subject: [PATCH 092/379] Minor doc additions --- .../root_system/fully_commutative_coxeter_elements.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py index 3060623c70d..565fca7110c 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -82,6 +82,8 @@ def find_left_descent(self, s): def has_left_descent(self, s): r""" Determine if ``s`` is a left descent of ``self``. + + OUTPUT: boolean """ return self.find_left_descent(s) is not None @@ -93,6 +95,14 @@ def left_descents(self): A set of integers representing the indices of generators which are left descents of ``self``. + + EXAMPLES: + + Determine the descents of an FC element in `A_5`:: + + sage: FC = FullyCommutativeCoxeterElements(['A', 5]) + sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).left_descents()) + [1, 4] """ m = self.parent().coxeter_matrix() out = set() From 13d4030999ed8da9f0a16c8d8eaef0c7cf8d3a25 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 04:05:17 -0600 Subject: [PATCH 093/379] Make FC elements iterator deterministic. Makes tests easier to write. --- .../fully_commutative_coxeter_elements.py | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py index 565fca7110c..b487d7eed80 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py @@ -296,24 +296,24 @@ class FullyCommutativeCoxeterElements(Parent): Enumerate the reduced words of fully commutative elements in `A_3` :: - sage: FC = FullyCommutativeReducedCoxeterWords(['A', 3]) + sage: FC = FullyCommutativeCoxeterElements(['A', 3]) sage: FC.category() Category of finite enumerated sets sage: FC.list() [[], + [1], [2], [3], - [1], - [2, 3], + [2, 1], + [1, 3], [1, 2], [3, 2], - [1, 3], - [2, 1], - [1, 2, 3], + [2, 3], [3, 2, 1], + [2, 1, 3], [1, 3, 2], - [2, 3, 1], - [2, 3, 1, 2]] + [1, 2, 3], + [2, 1, 3, 2]] Count the FC elements in `B_8` :: @@ -328,7 +328,7 @@ class FullyCommutativeCoxeterElements(Parent): sage: FC.category() Category of infinite enumerated sets sage: list(FC.iterate_to_length(2)) - [[], [1], [2], [0], [1, 0], [0, 2], [0, 1], [1, 2], [2, 1], [2, 0]] + [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] Constructing an element that is not fully commutative throws an error :: @@ -402,21 +402,27 @@ def __iter__(self): empty_word = self.element_class(self, [], check=False) letters = self.index_set() - recent_words = {empty_word} + # In the following, we use a dictionary's keys as a replacement for a + # set. this is because dictinary keys are guaranteed be ordered in + # Python 3.7+, so this is an easy way to make this iterator + # deterministic. + + recent_words = {empty_word: True} yield empty_word length = 1 while True: - new_words = set() - for w in recent_words: + new_words = {} + for w in recent_words.keys(): for s in letters: if w.still_reduced_fc_after_prepending(s): sw = self.element_class(self, [s] + list(w), check=False) - new_words.add(sw) + # "Add" sw to the "set" + new_words[sw] = True if len(new_words) == 0: return - for w in new_words: + for w in new_words.keys(): yield w recent_words = new_words length += 1 From ae43129ea40dd62b89746a32ae1ab0c56cd3112a Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 17:18:43 -0600 Subject: [PATCH 094/379] Move file out of root_system --- src/doc/en/reference/combinat/module_list.rst | 2 +- src/sage/combinat/__init__.py | 1 + src/sage/combinat/all.py | 2 ++ .../{root_system => }/fully_commutative_coxeter_elements.py | 4 ++-- src/sage/combinat/root_system/__init__.py | 1 - src/sage/combinat/root_system/all.py | 1 - 6 files changed, 6 insertions(+), 5 deletions(-) rename src/sage/combinat/{root_system => }/fully_commutative_coxeter_elements.py (99%) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 7c64f93c3dd..e1f19c81a42 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -121,6 +121,7 @@ Comprehensive Module list sage/combinat/expnums sage/combinat/family sage/combinat/fast_vector_partitions + sage/combinat/fully_commutative_coxeter_elements sage/combinat/finite_state_machine sage/combinat/finite_state_machine_generators sage/combinat/fqsym @@ -239,7 +240,6 @@ Comprehensive Module list sage/combinat/root_system/coxeter_group sage/combinat/root_system/coxeter_matrix sage/combinat/root_system/coxeter_type - sage/combinat/root_system/fully_commutative_coxeter_elements sage/combinat/root_system/dynkin_diagram sage/combinat/root_system/hecke_algebra_representation sage/combinat/root_system/integrable_representations diff --git a/src/sage/combinat/__init__.py b/src/sage/combinat/__init__.py index fe1de337006..0e5805395f3 100644 --- a/src/sage/combinat/__init__.py +++ b/src/sage/combinat/__init__.py @@ -18,6 +18,7 @@ - :ref:`sage.combinat.crystals` - :ref:`sage.combinat.root_system` - :ref:`sage.combinat.sf` + - :ref:`sage.combinat.fully_commutative_coxeter_elements` - :ref:`sage.combinat.counting` - :ref:`sage.combinat.enumerated_sets` diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 40f1e598466..05fa9fc6d26 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -218,6 +218,8 @@ # sine-Gordon Y-systems lazy_import('sage.combinat.sine_gordon', 'SineGordonYsystem') +lazy_import('sage.combinat.fully_commutative_coxeter_elements', 'FullyCommutativeCoxeterElements') + # Fully Packed Loop lazy_import('sage.combinat.fully_packed_loop', ['FullyPackedLoop', 'FullyPackedLoops']) diff --git a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py similarity index 99% rename from src/sage/combinat/root_system/fully_commutative_coxeter_elements.py rename to src/sage/combinat/fully_commutative_coxeter_elements.py index b487d7eed80..b0bd93e19d2 100644 --- a/src/sage/combinat/root_system/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -6,8 +6,8 @@ from sage.structure.list_clone import NormalizedClonableList from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from .coxeter_matrix import CoxeterMatrix -from .cartan_type import CartanType +from .root_system.coxeter_matrix import CoxeterMatrix +from .root_system.cartan_type import CartanType from collections import deque from sage.combinat.posets.posets import Poset import itertools diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index 520692903bf..b04c503fad1 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -58,7 +58,6 @@ - :ref:`sage.combinat.root_system.fundamental_group` - :ref:`sage.combinat.root_system.braid_move_calculator` - :ref:`sage.combinat.root_system.braid_orbit` -- :ref:`sage.combinat.root_system.fully_commutative_coxeter_elements` .. SEEALSO:: diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index 9c3825734de..ccb40064690 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -11,7 +11,6 @@ from .coxeter_matrix import CoxeterMatrix from .coxeter_type import CoxeterType from .root_system import RootSystem, WeylDim -from .fully_commutative_coxeter_elements import FullyCommutativeCoxeterElements lazy_import('sage.combinat.root_system.weyl_group', ['WeylGroup', 'WeylGroupElement']) lazy_import('sage.combinat.root_system.reflection_group_real', From 33906105900fca70b0393aba2d824de9c7f7b88a Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 19:03:45 -0600 Subject: [PATCH 095/379] Make descent methods multi-sided --- .../fully_commutative_coxeter_elements.py | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index b0bd93e19d2..5de684a9e82 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -49,7 +49,7 @@ def normalize(self): out_word = [] while len(self) > 0: - fronts = self.left_descents() + fronts = self.descents() out_word.extend(sorted(fronts)) for s in fronts: self.remove(s) @@ -58,7 +58,7 @@ def normalize(self): # Operations on fully commutative elements: - def find_left_descent(self, s): + def find_descent(self, s, side='left'): r""" Determine if ``s`` is a left descent of ``self``, and find the index of its occurrence in ``self``. @@ -68,46 +68,62 @@ def find_left_descent(self, s): - ``s`` -- integer; the index of the generator (element of ``self.parent().index_set()``). + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default 'left') if 'right', find ``s`` as a right + descent. + OUTPUT: - - The index of ``s`` in ``self`` if ``s`` is a left-descent, - ``None`` if ``s`` is not a left descent. + + The index of ``s`` in ``self`` if ``s`` is a descent on the appropriate + side, ``None`` if ``s`` is not a descent. """ m = self.parent().coxeter_matrix() - for (i, t) in enumerate(self): - if t == s and not any(m[x, t] > 2 for x in self[:i]): + view = list(self) if side == 'left' else self[::-1] + for (i, t) in enumerate(view): + if t == s and not any(m[x, t] > 2 for x in view[:i]): return i return None - def has_left_descent(self, s): + def has_descent(self, s, side='left'): r""" - Determine if ``s`` is a left descent of ``self``. + Determine if ``s`` is a descent on the appropriate side of ``self``. + + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default 'left') if 'right', determine if ``self`` + has ``s`` as a right descent. OUTPUT: boolean """ - return self.find_left_descent(s) is not None + return self.find_descent(s, side=side) is not None - def left_descents(self): + def descents(self, side='left'): r""" - Obtain the set of left descents of ``self``. + Obtain the set of left or right descents of ``self``. OUTPUT: A set of integers representing the indices of generators which are left descents of ``self``. + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default 'left') if 'right', find right descents. + EXAMPLES: - Determine the descents of an FC element in `A_5`:: + Determine the left descents of an FC element in `A_5`:: sage: FC = FullyCommutativeCoxeterElements(['A', 5]) - sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).left_descents()) + sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).descents()) [1, 4] """ + view = list(self) if side == 'left' else self[::-1] m = self.parent().coxeter_matrix() out = set() - for (i, t) in enumerate(self): - if not any(m[x, t] > 2 for x in self[:i]): + for (i, t) in enumerate(view): + if not any(m[x, t] > 2 for x in view[:i]): out.add(t) return out @@ -122,7 +138,7 @@ def still_reduced_fc_after_prepending(self, s): ``self.parent().index_set()``). """ m = self.parent().coxeter_matrix() - if self.has_left_descent(s): + if self.has_descent(s): return False # Find the first letter in that doesn't commute with s. @@ -135,7 +151,7 @@ def still_reduced_fc_after_prepending(self, s): u._set_list(self[j:]) for c in range(m[s, t] - 1): letter = t if c % 2 == 0 else s - i = u.find_left_descent(letter) + i = u.find_descent(letter) if i is not None: u.pop(i) else: From c000310432c70cb1614ba1dff9e03d4087e35f46 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 10 Jul 2020 02:02:00 +0000 Subject: [PATCH 096/379] Add coset decomposition and star ops --- .../fully_commutative_coxeter_elements.py | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index 5de684a9e82..56f6868fd98 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -274,6 +274,150 @@ def short_line_between(a, b): g.axes(False) return g + def coset_decomposition(self, J, side='left'): + r""" + Perform a coset decomposition of ``self`` with repsect to the subgroup + generated by ``J``. + + If self is `w`, then this method returns a tuple `(w_J, w^J)` such that + `w_J \in \langle J \rangle`, `w^J \not\in \langle J \rangle`, and `w = + w_J w^J`. + + .. NOTE: + + If ``J`` is generates a parabolic subgroup (i.e., ``J`` is a pair of + non-commuting generators), then the decomposition returned by this + function is unique. + + INPUT: + + - ``J`` -- collection of integers; the indices of the generators that + generate the subgroup with repsect to which to perform the + decomposition + + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default 'left') if 'right', perform a right coset + decomposition and return the tuple `(w^J, w_J)` where `w = w^J w_J`. + + EXAMPLES: + + Do a left and right coset decomposition in `B_6` with respect to the + parabolic subgroup generated by {5, 6} :: + + sage: FC = FullyCommutativeCoxeterElements(['B', 6]) + sage: x = FC([1, 6, 2, 5, 4, 6, 5]) + sage: x.coset_decomposition((5, 6)) + ([6, 5, 6], [1, 2, 4, 5]) + sage: x.coset_decomposition((5, 6), side='right') + ([1, 6, 2, 5, 4], [6, 5]) + """ + string = [] # The J-string + remaining = self.clone() # The remainder + + if side == 'right': + remaining._set_list(remaining[::-1]) + + while True: + x = next((x for x in J if remaining.has_descent(x, side='left')), None) + if x is not None: + string.append(x) + remaining.remove(x) + else: + break + + if side == 'right': + remaining._set_list(remaining[::-1]) + string = string[::-1] + + string = self.parent().element_class(self.parent(), string, check=False) + remaining.set_immutable() + + return (string, remaining) if side == 'left' else (remaining, string) + + def _star_operation_inner(self, J, direction, side): + mst = self.parent().coxeter_matrix()[J[0], J[1]] + # Decompose and assign the appropriate names based on the side of the decomposition. + if side == 'left': + (string, remaining) = self.coset_decomposition(J, side=side) + elif side == 'right': + (remaining, string) = self.coset_decomposition(J, side=side) + + cur_string = list(string) + + if direction == 'down' and 2 <= len(string) <= mst - 1: + # Decrease the length of the J-string + new_string = cur_string[1:] if side == 'left' else cur_string[:-1] + elif direction == 'up' and 1 <= len(string) <= mst - 2: + # Increase the length of the J-string + ending_letter = cur_string[0] if side == 'left' else cur_string[-1] + other = next(x for x in J if x != ending_letter) + new_string = [other] + cur_string if side == 'left' else cur_string + [other] + else: + return None + + # concatenate in the appropriate order + combined_data = new_string + list(remaining) if side == 'left' else list(remaining) + new_string + + return self.parent().element_class(self.parent(), combined_data, check=False) + + def upper_star(self, J, side='left'): + r""" + Perform an upper star operation on ``self`` with respect to the + parabolic subgroup generated by ``J``. + + If ``self`` is `w` and `w` has the (unique) coset decomposition `w_J + w^J` (where `w_J` is necessarily an alternating string in `\{s, t\}` + where `J = \{s, t\}`), an upper star operation on `w` increases the + length of `w_J` by adding element on the left (if such an addition would + not result in a long braid), and a lower star operation decreases the + length of `w_J` by removing the left-most element (if such an removal + would not result in `w_J` being empty). + + INPUT: + + - ``J`` -- tuple of integers; a *pair* of indices of *non-commuting* + generators, such that ``J`` generates a parabolic subgroup. + + OPTIONAL ARGUEMNTS: + + - ``side`` -- string (default 'left') if 'right', perform a right upper + star operation. + + OUTPUT: + + The result of the star operation if it is defined on ``self``, otherwise + ``None``. + + EXAMPLES: + + Perform right star operations on an element in `B_6`, with repsect to + the parabolic subgroup `\la 5, 6 \ra` (the long braid pair). Here both + star operations are defined :: + + sage: FC = FullyCommutativeCoxeterElements(['B', 6]) + sage: x = FC([1, 6, 2, 5, 4, 6, 5]) + sage: x.upper_star((5, 6), side='right') + [1, 6, 2, 5, 4, 6, 5, 6] + sage: x.lower_star((5, 6), side='right') + [1, 6, 2, 5, 4, 6] + + For the same element, a left upper star operation is not defined :: + + sage: FC = FullyCommutativeCoxeterElements(['B', 6]) + sage: x = FC([1, 6, 2, 5, 4, 6, 5]) + sage: x.upper_star((5, 6)) is None + True + """ + return self._star_operation_inner(J, 'up', side) + + def lower_star(self, J, side='left'): + r""" + Perform a lower star operation on ``self``; see :ref:`upper_star` for + definitions and documentation. + """ + return self._star_operation_inner(J, 'down', side) + class FullyCommutativeCoxeterElements(Parent): r""" From 240213e474c53b6c8a1c6680b28c1a9345e91961 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 9 Jul 2020 20:08:36 -0600 Subject: [PATCH 097/379] Minor doc fixes --- src/sage/combinat/fully_commutative_coxeter_elements.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index 56f6868fd98..19458c409f1 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -169,7 +169,7 @@ def heap(self, **kargs): OPTIONAL ARGUMENTS: - ``one_index`` -- boolean (default False); make the Poset on the - indices {1, 2, ..., n} instead of {0, 1, ..., n-1}. + indices `\{1, 2, \dots, n\}` instead of `\{0, 1, \dots, n-1\}`. - ``display_labeling`` -- boolean (default False); make the elements of the resulting Poset be tuples `(i, w_i)` where `w_i` is the letter at @@ -285,7 +285,7 @@ def coset_decomposition(self, J, side='left'): .. NOTE: - If ``J`` is generates a parabolic subgroup (i.e., ``J`` is a pair of + If ``J`` generates a parabolic subgroup (i.e., ``J`` is a pair of non-commuting generators), then the decomposition returned by this function is unique. @@ -392,7 +392,7 @@ def upper_star(self, J, side='left'): EXAMPLES: Perform right star operations on an element in `B_6`, with repsect to - the parabolic subgroup `\la 5, 6 \ra` (the long braid pair). Here both + the parabolic subgroup `\langle 5, 6 \rangle` (the long braid pair). Here both star operations are defined :: sage: FC = FullyCommutativeCoxeterElements(['B', 6]) @@ -413,7 +413,7 @@ def upper_star(self, J, side='left'): def lower_star(self, J, side='left'): r""" - Perform a lower star operation on ``self``; see :ref:`upper_star` for + Perform a lower star operation on ``self``; see ``.upper_star`` for definitions and documentation. """ return self._star_operation_inner(J, 'down', side) From 5a1e43d4d808d5d57e643677ca981e0d6c808132 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 10 Jul 2020 03:59:32 -0600 Subject: [PATCH 098/379] Simplify plotting --- .../fully_commutative_coxeter_elements.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index 19458c409f1..b0ede8fb384 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -237,20 +237,11 @@ def plot(self): OUTPUT: GraphicsObject """ import sage.plot.all as plot - from sage.modules.free_module_element import vector m = self.parent().coxeter_matrix() letters = self.parent().index_set() graphics = [] - # Returns a line between two points that respects the space of the "nodes" - def short_line_between(a, b): - a, b = vector(a), vector(b) - d = (b - a).normalized() - a2 = a + 0.1*d - b2 = b - 0.1*d - return plot.line([a2, b2], color='black') - h = self.heap() levels = h.level_sets() letters_at_level = [set(self[i] for i in level) for level in levels] @@ -261,14 +252,14 @@ def short_line_between(a, b): x = self[i] # Draw the node - graphics.append(plot.circle((x, level), 0.1)) - graphics.append(plot.text(str(x), (x, level))) + graphics.append(plot.circle((x, level), 0.1, fill=True, facecolor='white', edgecolor='blue', zorder=1)) + graphics.append(plot.text(str(x), (x, level), color='blue', zorder=2)) neighbors = {z for z in letters if m[x, z] >= 3} for other in neighbors: highest_level = max((j + 1 for j in range(level_zero_index) if other in letters_at_level[j]), default=None) if highest_level: - graphics.append(short_line_between((other, highest_level), (x, level))) + graphics.append(plot.line([(other, highest_level), (x, level)], color='black', zorder=0)) g = sum(graphics) g.axes(False) From c855da1ae9da4160d32cfca8abd4c09467129dac Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 10 Jul 2020 04:17:30 -0600 Subject: [PATCH 099/379] Add star closure. --- .../fully_commutative_coxeter_elements.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index b0ede8fb384..d0109404acd 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -409,6 +409,64 @@ def lower_star(self, J, side='left'): """ return self._star_operation_inner(J, 'down', side) + def star_closure(self, side='left', **kargs): + r""" + Compute the star operation closure of ``self``. + + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default 'left') if 'right', compute the right star + closure. + - ``upper_only`` -- boolean (default False) if passed, compute the upper + star closure. + - ``lower_only`` -- boolean (default False) if passed, compute the lower + star closure. + + OUTPUT: set; the (upper, lower, or both) star closure of ``self``. + + EXAMPLES: + + Compute the left star closure of [1] in the group `I_8`. This should be + set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same + as the left *upper* star closure :: + + sage: FC = FullyCommutativeCoxeterElements(['I', 8]) + sage: sorted(FC([1]).star_closure()) + [[1], + [1, 2, 1], + [1, 2, 1, 2, 1], + [1, 2, 1, 2, 1, 2, 1], + [2, 1], + [2, 1, 2, 1], + [2, 1, 2, 1, 2, 1]] + sage: FC([1]).star_closure() == FC([1]).star_closure(upper_only=True) + True + """ + m = self.parent().coxeter_matrix() + adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] + + directions = {'up', 'down'} + if 'upper_only' in kargs and kargs['upper_only']: + directions = {'up'} + elif 'lower_only' in kargs and kargs['lower_only']: + directions = {'down'} + + closure = {self} + recent_words = {self} + while True: + new_words = set() + for w in recent_words: + for J in adjacent_pairs: + for d in directions: + n = w._star_operation_inner(J, d, side) + if n is not None and n not in closure: + new_words.add(n) + if len(new_words) == 0: + break + closure.update(new_words) + recent_words = new_words + return closure + class FullyCommutativeCoxeterElements(Parent): r""" From ee82db6eb017bbbf6562d62260ee48b0b9ce900f Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Fri, 10 Jul 2020 18:49:14 -0600 Subject: [PATCH 100/379] add reference for fully commutative elements of Coxeter groups --- src/doc/en/reference/references/index.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 92c40cecf27..3b0ff5a280d 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2485,6 +2485,9 @@ REFERENCES: .. [GR2001] Chris Godsil and Gordon Royle, *Algebraic Graph Theory*. Graduate Texts in Mathematics, Springer, 2001. +.. [Gre2006] \R. M. Green, *Star reducible Coxeter groups*, Glasgow + Mathematical Journal, Volume 48, Issue 3, pp. 583-609. + .. [Gr2006] Matthew Greenberg, "Heegner points and rigid analytic modular forms", Ph.D. Thesis, McGill University, 2006. @@ -5074,6 +5077,13 @@ REFERENCES: Anders Björner, Research Paper 9, 24 pp. +.. [Ste1996] John R. Stembridge, *On the fully commutative elements of Coxeter + groups*, Journal of Algebraic Combinatorics, 5, pp. 355-385 + +.. [Ste1998] John R. Stembridge, *The enumeration of the fully commutative + elements of Coxeter groups*, Journal of Algebraic Combinatorics, + 7, pp. 291-320. + .. [Ste2003] John R. Stembridge, *A local characterization of simply-laced crystals*, Transactions of the American Mathematical Society, Vol. 355, No. 12 (Dec., 2003), From 28081b6ee60c4ba3ce497c9cba1673d462835503 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Fri, 10 Jul 2020 19:01:04 -0600 Subject: [PATCH 101/379] add docstrings --- .../fully_commutative_coxeter_elements.py | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index d0109404acd..e4047f6a5fa 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -1,5 +1,10 @@ r""" -Fully commutative Coxeter group elements +Fully commutative elements of Coxeter groups + +An element $w$ in a Coxeter system (W,S) is fully commutative (FC) if +every two reduced word of w can be related by a sequence of only +commutation relations, i.e., relations of the form $st=ts$ where $s,t$ are +commuting generators in $S$. See [Ste1996]_. """ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -12,19 +17,27 @@ from sage.combinat.posets.posets import Poset import itertools -class FullyCommutativeCoxeterElement(NormalizedClonableList): +class FullyCommutativeElement(NormalizedClonableList): r""" - A (reduced word of a) fully commutative element in the Coxeter system. - """ + A (reduced word of a) fully commutative (FC) element in a Coxeter system. + + An element $w$ in a Coxeter system (W,S) is fully commutative (FC) if + every two reduced word of w can be related by a sequence of only + commutation relations, i.e., relations of the form $st=ts$ where $s,t$ are + commuting generators in $S$. See [Ste1996]_. + """ + # Methods required as a subclass of NormalizedClonableList: def check(self): r""" - Determine if the word ``self`` actually represents a fully commutative - reduced word. Raises a ValueError if this is not the case. + Check if ``self`` is the reduced word of an FC element. + + .. NOTE:: + """ if not self.parent()._is_fully_commutative(self._get_list()): - raise ValueError('list does not represent a fully commutative word.') + raise ValueError('The word does not represent a fully commutative element.') def normalize(self): r""" @@ -581,7 +594,7 @@ def __init__(self, data): def _element_constructor_(self, lst): return self.element_class(self, lst) - Element = FullyCommutativeCoxeterElement + Element = FullyCommutativeElement def coxeter_matrix(self): r""" From 802723c87f4c48a2b58ddb08fabfd9e909df960c Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sat, 11 Jul 2020 01:00:36 -0600 Subject: [PATCH 102/379] Rename parent class as well. --- src/sage/combinat/all.py | 2 +- .../fully_commutative_coxeter_elements.py | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 05fa9fc6d26..4e7faa04c06 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -218,7 +218,7 @@ # sine-Gordon Y-systems lazy_import('sage.combinat.sine_gordon', 'SineGordonYsystem') -lazy_import('sage.combinat.fully_commutative_coxeter_elements', 'FullyCommutativeCoxeterElements') +lazy_import('sage.combinat.fully_commutative_coxeter_elements', 'FullyCommutativeElements') # Fully Packed Loop lazy_import('sage.combinat.fully_packed_loop', ['FullyPackedLoop', 'FullyPackedLoops']) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index e4047f6a5fa..7b935141c9a 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -47,7 +47,7 @@ def normalize(self): Observe the normal form of three equivalent FC words in `B_5` :: - sage: FC = FullyCommutativeCoxeterElements(['B', 5]) + sage: FC = FullyCommutativeElements(['B', 5]) sage: FC([1, 4, 3, 5, 2, 4, 3]) [1, 4, 3, 5, 2, 4, 3] sage: FC([4, 1, 3, 5, 2, 4, 3]) @@ -128,7 +128,7 @@ def descents(self, side='left'): Determine the left descents of an FC element in `A_5`:: - sage: FC = FullyCommutativeCoxeterElements(['A', 5]) + sage: FC = FullyCommutativeElements(['A', 5]) sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).descents()) [1, 4] """ @@ -201,7 +201,7 @@ def heap(self, **kargs): Create the heap of a fully commutative element in `A_5` :: - sage: FC = FullyCommutativeCoxeterElements(['A', 5]) + sage: FC = FullyCommutativeElements(['A', 5]) sage: FC([1, 4, 3, 5, 2, 4]).heap().cover_relations() [[1, 2], [1, 3], [2, 5], [2, 4], [3, 5], [0, 4]] sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() @@ -309,7 +309,7 @@ def coset_decomposition(self, J, side='left'): Do a left and right coset decomposition in `B_6` with respect to the parabolic subgroup generated by {5, 6} :: - sage: FC = FullyCommutativeCoxeterElements(['B', 6]) + sage: FC = FullyCommutativeElements(['B', 6]) sage: x = FC([1, 6, 2, 5, 4, 6, 5]) sage: x.coset_decomposition((5, 6)) ([6, 5, 6], [1, 2, 4, 5]) @@ -399,7 +399,7 @@ def upper_star(self, J, side='left'): the parabolic subgroup `\langle 5, 6 \rangle` (the long braid pair). Here both star operations are defined :: - sage: FC = FullyCommutativeCoxeterElements(['B', 6]) + sage: FC = FullyCommutativeElements(['B', 6]) sage: x = FC([1, 6, 2, 5, 4, 6, 5]) sage: x.upper_star((5, 6), side='right') [1, 6, 2, 5, 4, 6, 5, 6] @@ -408,7 +408,7 @@ def upper_star(self, J, side='left'): For the same element, a left upper star operation is not defined :: - sage: FC = FullyCommutativeCoxeterElements(['B', 6]) + sage: FC = FullyCommutativeElements(['B', 6]) sage: x = FC([1, 6, 2, 5, 4, 6, 5]) sage: x.upper_star((5, 6)) is None True @@ -443,7 +443,7 @@ def star_closure(self, side='left', **kargs): set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same as the left *upper* star closure :: - sage: FC = FullyCommutativeCoxeterElements(['I', 8]) + sage: FC = FullyCommutativeElements(['I', 8]) sage: sorted(FC([1]).star_closure()) [[1], [1, 2, 1], @@ -481,7 +481,7 @@ def star_closure(self, side='left', **kargs): return closure -class FullyCommutativeCoxeterElements(Parent): +class FullyCommutativeElements(Parent): r""" The combinatorial class of reduced words of fully commutative elements in a Coxeter group. @@ -518,7 +518,7 @@ class FullyCommutativeCoxeterElements(Parent): Enumerate the reduced words of fully commutative elements in `A_3` :: - sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC = FullyCommutativeElements(['A', 3]) sage: FC.category() Category of finite enumerated sets sage: FC.list() @@ -539,14 +539,14 @@ class FullyCommutativeCoxeterElements(Parent): Count the FC elements in `B_8` :: - sage: FC = FullyCommutativeCoxeterElements(['B', 8]) + sage: FC = FullyCommutativeElements(['B', 8]) sage: len(FC) # long time (7 seconds) 14299 Iterate through the fully commutative elements of length up to 3 in the non FC-finite group affine `A_2` :: - sage: FC = FullyCommutativeCoxeterElements(['A', 2, 1]) + sage: FC = FullyCommutativeElements(['A', 2, 1]) sage: FC.category() Category of infinite enumerated sets sage: list(FC.iterate_to_length(2)) @@ -554,7 +554,7 @@ class FullyCommutativeCoxeterElements(Parent): Constructing an element that is not fully commutative throws an error :: - sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC = FullyCommutativeElements(['A', 3]) sage: FC([1,2,1]) Traceback (most recent call last): ... @@ -562,7 +562,7 @@ class FullyCommutativeCoxeterElements(Parent): Elements are normalized to Cartier--Foata normal form upon construction :: - sage: FC = FullyCommutativeCoxeterElements(['A', 3]) + sage: FC = FullyCommutativeElements(['A', 3]) sage: FC([2, 3, 1, 2]) [2, 1, 3, 2] From 39fc646d3abd37b378cee2525bef0d5f90fd7a88 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sat, 11 Jul 2020 01:00:51 -0600 Subject: [PATCH 103/379] Change exception to match new wording in test. --- src/sage/combinat/fully_commutative_coxeter_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_coxeter_elements.py index 7b935141c9a..be952b125fb 100644 --- a/src/sage/combinat/fully_commutative_coxeter_elements.py +++ b/src/sage/combinat/fully_commutative_coxeter_elements.py @@ -37,7 +37,7 @@ def check(self): """ if not self.parent()._is_fully_commutative(self._get_list()): - raise ValueError('The word does not represent a fully commutative element.') + raise ValueError('list does not represent a fully commutative word.') def normalize(self): r""" From 99d7dc342c808eb66f97684e6a40b11697b587c6 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sat, 11 Jul 2020 07:11:39 +0000 Subject: [PATCH 104/379] Rename file --- src/doc/en/reference/combinat/module_list.rst | 2 +- src/sage/combinat/__init__.py | 2 +- src/sage/combinat/all.py | 2 +- ...tative_coxeter_elements.py => fully_commutative_elements.py} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/sage/combinat/{fully_commutative_coxeter_elements.py => fully_commutative_elements.py} (100%) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index e1f19c81a42..50c13d7deee 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -121,7 +121,7 @@ Comprehensive Module list sage/combinat/expnums sage/combinat/family sage/combinat/fast_vector_partitions - sage/combinat/fully_commutative_coxeter_elements + sage/combinat/fully_commutative_elements sage/combinat/finite_state_machine sage/combinat/finite_state_machine_generators sage/combinat/fqsym diff --git a/src/sage/combinat/__init__.py b/src/sage/combinat/__init__.py index 0e5805395f3..ac8f9b4a304 100644 --- a/src/sage/combinat/__init__.py +++ b/src/sage/combinat/__init__.py @@ -18,7 +18,7 @@ - :ref:`sage.combinat.crystals` - :ref:`sage.combinat.root_system` - :ref:`sage.combinat.sf` - - :ref:`sage.combinat.fully_commutative_coxeter_elements` + - :ref:`sage.combinat.fully_commutative_elements` - :ref:`sage.combinat.counting` - :ref:`sage.combinat.enumerated_sets` diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 4e7faa04c06..5d73a4072e4 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -218,7 +218,7 @@ # sine-Gordon Y-systems lazy_import('sage.combinat.sine_gordon', 'SineGordonYsystem') -lazy_import('sage.combinat.fully_commutative_coxeter_elements', 'FullyCommutativeElements') +lazy_import('sage.combinat.fully_commutative_elements', 'FullyCommutativeElements') # Fully Packed Loop lazy_import('sage.combinat.fully_packed_loop', ['FullyPackedLoop', 'FullyPackedLoops']) diff --git a/src/sage/combinat/fully_commutative_coxeter_elements.py b/src/sage/combinat/fully_commutative_elements.py similarity index 100% rename from src/sage/combinat/fully_commutative_coxeter_elements.py rename to src/sage/combinat/fully_commutative_elements.py From b2581f861ee8fc61d2c553423bdf6d1baf89889c Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 13:27:29 -0600 Subject: [PATCH 105/379] add reference for n-value of fc elements --- src/doc/en/reference/references/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3b0ff5a280d..5c6c0536695 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2488,6 +2488,10 @@ REFERENCES: .. [Gre2006] \R. M. Green, *Star reducible Coxeter groups*, Glasgow Mathematical Journal, Volume 48, Issue 3, pp. 583-609. +.. [GX2020] \R. M. Green, Tianyuan Xu, *Classification of Coxeter groups with + finitely many elements of a-value 2*, Algebraic Combinatorics, + Volume 3 (2020) no. 2, pp. 331-364. + .. [Gr2006] Matthew Greenberg, "Heegner points and rigid analytic modular forms", Ph.D. Thesis, McGill University, 2006. From 3aadcd9750e30164f1c1b400eedba20a53a9fc97 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 13:29:17 -0600 Subject: [PATCH 106/379] add docstrings up to plot for fc element --- .../combinat/fully_commutative_elements.py | 398 +++++++++++++----- 1 file changed, 298 insertions(+), 100 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index be952b125fb..8710201958a 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -24,7 +24,14 @@ class FullyCommutativeElement(NormalizedClonableList): An element $w$ in a Coxeter system (W,S) is fully commutative (FC) if every two reduced word of w can be related by a sequence of only commutation relations, i.e., relations of the form $st=ts$ where $s,t$ are - commuting generators in $S$. See [Ste1996]_. + commuting generators in $S$. Equivalently, $w$ is FC if and only if for + every pair of generators $s,t \in S$ for which $m(s,t)>2$, no reduced word + of $w$ contains the "braid" word $sts...$ of length $m(s,t)$ as a contiguous + subword. See [Ste1996]_. We will use the braid-avoidance criterion to + check if an element is FC. + + Every FC element has a canonical reduced word called its Cartier--Foata + form. See [Gre2006]_. We will normalize each FC element to this form. """ @@ -33,29 +40,137 @@ def check(self): r""" Check if ``self`` is the reduced word of an FC element. - .. NOTE:: + EXAMPLES: + To construct an FC element, first call the parent class + FullyCommutativeElements. The parent class contains information about + the Coxeter matrix of the ambient Coxeter system :: + + sage: FC = FullyCommutativeElements(['B', 3]) + sage: FC.coxeter_matrix() + [1 3 2] + [3 1 4] + [2 4 1] + + We can construct FC elements as follows :: + + sage: FC([]) + [] + sage: FC([1,2]) + [1,2] + sage: FC([2,3,2]) + [2,3,2] + sage: FC([3,2,3]) + [3,2,3] + + The output is the normalized form of ``self``, which may be a + different reduced word of the element represented by the input :: + + sage: FC([3,1]) + [1,3] + sage: FC([2,3,1]) + [2,1,3] + sage: FC([1,3]) == FC([3,1]) + True + + If the input is not the reduced word of an FC element, return a + ValueEror :: + + sage: FC([1,2,1]) + ValueError Traceback (most recent call last) + ... + ValueError: The input is not a reduced word of a fully commutative + elements. + + sage: FC([2,3,2,3]) + ValueError Traceback (most recent call last) + ... + ValueError: The input is not a reduced word of a fully commutative + elements. + + .. SEEALSO:: + :func:`_is_fully_commutative` + :func:`normalize` """ - if not self.parent()._is_fully_commutative(self._get_list()): - raise ValueError('list does not represent a fully commutative word.') + if not self._is_fully_commutative(self._get_list()): + raise ValueError('The input is not a reduced word of a fully commutative element.') def normalize(self): r""" - Normalize ``self`` to Cartier--Foata normal form. + Normalize ``self`` to the Cartier--Foata normal form. + """ + return cartier_foata_form(self) + + + def _is_fully_commutative(self): + r""" + Determine if ``self`` represents an FC element via the braid-avoidance + criterion. + """ + matrix = self.parent().coxeter_matrix() + w = self + + def contains_long_braid(w): + for i in range(0, len(w)-2): + a = w[i] + b = w[i+1] + m = matrix[a, b] + if m > 2 and i+m <= len(w): + ab_braid = [a, b] * (m // 2) + ([a,] if m % 2 == 1 else []) + if w[i:i+m] == ab_braid: + return True + return False + + def commute_once(word, i): + return word[:i] + (word[i+1], word[i]) + word[i+2:] + + if contains_long_braid(w): + return False + else: + l, checked, queue = len(w), {w}, deque([w]) + while queue: + word = queue.pop() + for i in range(l-1): + a, b = word[i], word[i+1] + if matrix[a, b] == 2: + new_word = commute_once(word, i) + if new_word not in checked: + if contains_long_braid(new_word): + return False + else: + checked.add(new_word) + queue.appendleft(new_word) + return True + + + def cartier_foata_form(self): + r""" + Return the Cartier--Foata form of ``self``. + EXAMPLES: - Observe the normal form of three equivalent FC words in `B_5` :: + The following reduced words express the same FC elements in `B_5` :: sage: FC = FullyCommutativeElements(['B', 5]) - sage: FC([1, 4, 3, 5, 2, 4, 3]) + sage: FC([1, 4, 3, 5, 2, 4, 3]).cartier_foata_form() [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 1, 3, 5, 2, 4, 3]) + sage: FC([4, 1, 3, 5, 2, 4, 3]).cartier_foata_form() [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 1, 5, 3, 4, 2, 3]) + sage: FC([4, 3, 1, 5, 4, 2, 3]).cartier_foata_form() [1, 4, 3, 5, 2, 4, 3] - sage: FC([1, 4, 3, 5, 2, 4, 3]) == FC([4, 1, 3, 5, 2, 4, 3]) == FC([4, 1, 5, 3, 4, 2, 3]) - True + + .. NOTE:: + The Cartier--Foata form of a reduced word of an FC element w can be + found recursively by repeatedly moving left descents of elements + to the left and ordering the left descents from small to large. In + the above example, the left descents of the element are 4 and 1, + therefore the Cartier--Foata form of the element is the + concatenation of [1,4] with the Cartier--Foata form of the + remaining part of the word. See [Gre2006]_. + + .. SEEALSO:: + :func:`descents` """ self._require_mutable() @@ -73,23 +188,50 @@ def normalize(self): def find_descent(self, s, side='left'): r""" - Determine if ``s`` is a left descent of ``self``, and find the index of - its occurrence in ``self``. + Return the index of a left descent ``s`` of ``self``. INPUT: - - ``s`` -- integer; the index of the generator (element of - ``self.parent().index_set()``). + - ``s`` -- integer representing a generator of the Coxeter system. + + OUTPUT: + + Determine if the generator ``s`` is a left descent of ``self``; return + the index of the leftmost occurrence of ``s`` in ``self`` if so and + return ``None`` if not. OPTIONAL ARGUMENTS: - - ``side`` -- string (default 'left') if 'right', find ``s`` as a right - descent. + - ``side`` -- string (default: 'left'); if the argument is set + to 'right', the function checks if ``s`` is a right descent of + ``self`` and finds the index of the rightmost occurrence of ``s`` + if so. - OUTPUT: - The index of ``s`` in ``self`` if ``s`` is a descent on the appropriate - side, ``None`` if ``s`` is not a descent. + EXAMPLES:: + + sage: FC = FullyCommutativeElements(['B', 5]) + sage: w = FC([1, 4, 3, 5, 2, 4, 3]) + sage: w.find_descent(1) + 0 + sage: w.find_descent(1, side='right') + None + sage: w.find_descent(4) + 1 + sage: w.find_descent(4, side='right') + None + sage: w.find_descent(3) + None + sage: w.find_descent(4, side='right') + 6 + + .. NOTE:: + A generator $s$ is a left descent of an FC element $w$ if + and only if for one (equivalently, every) reduced word of $w$, $s$ + appears to in the word and every generator to the left of the + leftmost $s$ in the word commutes with $s$. A similar result holds + for right descents of FC elements. + """ m = self.parent().coxeter_matrix() view = list(self) if side == 'left' else self[::-1] @@ -102,35 +244,54 @@ def has_descent(self, s, side='left'): r""" Determine if ``s`` is a descent on the appropriate side of ``self``. + OUTPUT: a boolean value + OPTIONAL ARGUMENTS: - - ``side`` -- string (default 'left') if 'right', determine if ``self`` - has ``s`` as a right descent. + - ``side`` -- string (default: 'left'); if set to 'right', + determine if ``self`` has ``s`` as a right descent. + + EXAMPLES:: - OUTPUT: boolean + sage: FC = FullyCommutativeElements(['B', 5]) + sage: w = FC([1, 4, 3, 5, 2, 4, 3]) + sage: w.has_descent(1) + True + sage: w.has_descent(1, side='right') + False + sage: w.has_descent(4) + True + sage: w.has_descent(4, side='right') + False + + .. SEEALSO:: + :func:`find_descent` """ return self.find_descent(s, side=side) is not None def descents(self, side='left'): r""" - Obtain the set of left or right descents of ``self``. - - OUTPUT: - - A set of integers representing the indices of generators which are left - descents of ``self``. + Obtain the set of descents on the appropriate side of ``self``. OPTIONAL ARGUMENTS: - - ``side`` -- string (default 'left') if 'right', find right descents. + - ``side`` -- string (default: 'left'); if set to 'right', + find the right descents. - EXAMPLES: + EXAMPLES:: - Determine the left descents of an FC element in `A_5`:: - sage: FC = FullyCommutativeElements(['A', 5]) - sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).descents()) - [1, 4] + sage: FC = FullyCommutativeElements(['B', 5]) + sage: w = FC([1, 4, 3, 5, 2, 4, 3]) + sage: w.descents() + {1, 4} + sage: w.descents(side='right') + {3} + + .. NOTE:: + See the chacterization of descents for FC elements in + `find_descent`. + """ view = list(self) if side == 'left' else self[::-1] m = self.parent().coxeter_matrix() @@ -142,14 +303,71 @@ def descents(self, side='left'): def still_reduced_fc_after_prepending(self, s): r""" - Determine if ``self`` prepended with ``s`` is still a reduced word of a - fully commutative element in the Coxeter system. + Determine if ``self`` prepended with ``s`` is still a reduced word of + an FC element in the Coxeter system. INPUT: - - ``s`` -- integer; the index of the generator (element of - ``self.parent().index_set()``). + - ``s`` -- integer representing a generator of the Coxeter system. + - ``self`` -- a reduced word of an FC element + + + EXAMPLES:: + + sage: FCB3 = FullyCommutativeElements(['B', 3]) + sage: FCB3.coxeter_matrix() + [1 3 2] + [3 1 4] + [2 4 1] + sage: w = FCB3([1,2]) + sage: w.still_reduced_fc_after_prepending(1) + False + sage: w.still_reduced_fc_after_prepending(2) + False + sage: w.still_reduced_fc_after_prepending(3) + True + sage: u = FC([3,1,2]) + sage: u.still_reduced_fc_after_prepending(1) + False + sage: u.still_reduced_fc_after_prepending(2) + True + sage: u.still_reduced_fc_after_prepending(3) + False + + sage: FCA5 = FullyCommutativeElements(['A',5]) + sage: w = FCA5([2,4,1,3,2,5]) + sage: w.still_reduced_fc_after_prepending(5) + True + + + .. NOTE:: + If $w$ is a reduced word of an element, then the concatenation + $sw$ is still a reduced word if and only if $s$ is not a left + descent of $w$ by general Coxeter group theory. So now assume $w$ + is a reduced word of an FC element and $s$ is not a left descent + $w$. In this case, Lemma 4.1 of [Ste1996]_ implies that $sw$ is + not a reduced word of an FC element if and only if some letter in + $w$ does not commute with $s$ and the following conditions + hold simultaneously for the leftmost such letter $t$: + + (1) $t$ is left descent of the word $u_1$ obtained by + removing the leftmost $s$ from $w$; + (2) $t$ is left descent of the word $u_2$ obtained by + removing the leftmost $t$ from $u_1$; + ... + (m-1) the appropriate element in $\{s, t\}$ is a left descent + of the word $u_{m-1}$ obtained by removing the leftmost letter + required to be a descent in Condition (m-2) from $u_{m-2}$. + + In the last example above, we have $s=5$, $t=4$, Condition (1) + holds, but Condition (2) fails, therefore $5w$ is still a + reduced word of an FC element. + + REFERENCES: + + See Lemma 4.1 of [Ste1996]_. """ + m = self.parent().coxeter_matrix() if self.has_descent(s): return False @@ -174,28 +392,30 @@ def still_reduced_fc_after_prepending(self, s): def heap(self, **kargs): r""" - Create the heap of ``self``. + Create the heap poset of ``self``. - The heap of a fully commutative word `w` is a partial order on the - indices of a reduced word for `w`. + The heap of an FC element $w$ is a labeled poset that can be defined + from any reduced word of $w$. Different reduced words yield + isomorphic labeled posets, so the heap is well defined. - OPTIONAL ARGUMENTS: + Input: - - ``one_index`` -- boolean (default False); make the Poset on the - indices `\{1, 2, \dots, n\}` instead of `\{0, 1, \dots, n-1\}`. + - ``self`` -- list, a reduced word $w=s_0... s_{k-1}$ of an FC element. - - ``display_labeling`` -- boolean (default False); make the elements of - the resulting Poset be tuples `(i, w_i)` where `w_i` is the letter at - index `i` in ``self``. + OUTPUT: + A labeled poset where the underlying set is $\{0,1,...,k-1\}$ and + where each element $i$ carries $s_i$ as its label. - .. NOTE: + OPTIONAL ARGUMENTS: + + - ``one_index`` -- boolean (default: False). Setting the value + to True will change the underlying set of the poset to $\{1, 2, + \dots, n\}$. - Fully commutative elements have the property that the heap of any - reduced word is unique up to Poset isomorphism. Of course, the - particular one returned from this method is created from the - Cartier--Foata normal form of the reduced word. + - ``display_labeling`` -- boolean (default: False). Setting + the value to True will display the label $s_i$ for each element $i$ + of the poset. - OUTPUT: Poset; the partially ordered set on the indices of ``self``. EXAMPLES: @@ -206,23 +426,22 @@ def heap(self, **kargs): [[1, 2], [1, 3], [2, 5], [2, 4], [3, 5], [0, 4]] sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() [[2, 3], [2, 4], [3, 6], [3, 5], [4, 6], [1, 5]] + + .. NOTE:: + The partial order in the heap is defined by declaring $i\prec j$ + if $i 2 and i+m <= len(w): - ab_braid = (a, b) * (m // 2) + ((a,) if m % 2 == 1 else ()) - if w[i:i+m] == ab_braid: - return True - return False - - def commute_once(word, i): - return word[:i] + (word[i+1], word[i]) + word[i+2:] - - w = tuple(w) - - if contains_long_braid(w): - return False - else: - l, checked, queue = len(w), {w}, deque([w]) - while queue: - word = queue.pop() - for i in range(l-1): - a, b = word[i], word[i+1] - if matrix[a, b] == 2: - new_word = commute_once(word, i) - if new_word not in checked: - if contains_long_braid(new_word): - return False - else: - checked.add(new_word) - queue.appendleft(new_word) - return True From ac260cd3541f879c02ad820a67b310daa524c4a1 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 14:41:41 -0600 Subject: [PATCH 107/379] more docstring edits --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 8710201958a..71a55118bdb 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -459,7 +459,7 @@ def n_value(self): [GX2020]_. EXAMPLES: - sage: FC = FullyCommutativeCoxeterElements(['A', 5]) + sage: FC = FullyCommutativeElements(['A', 5]) sage: FC([1,3]).n_value() 2 sage: FC([1,2,3]).n_value() From 2bd48fd1fc9ae0cf9d652a6dd565896d8281225f Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 14:59:18 -0600 Subject: [PATCH 108/379] more docstring edits --- .../combinat/fully_commutative_elements.py | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 71a55118bdb..ff42a6b8e7a 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -717,45 +717,35 @@ def star_closure(self, side='left', **kargs): class FullyCommutativeElements(Parent): r""" - The combinatorial class of reduced words of fully commutative elements in a - Coxeter group. + Class for the set of fully commutative (FC) elements of a Coxeter systems - Given a Coxeter system with Coxeter matrix `m`, a *fully commutative* - element `w` is an element with the property that any reduced word for `w` - can be transformed into any other reduced word for `w` by applying - commutation relations only. Equivalently, `w` is an element such that no - reduced word for `w` contains a long braid. - - Fully commutative elements of a Coxeter system have a unique canonical form - called the *Cartier--Foata normal form*. All elements of this class are - automatically normalized to Cartier--Foata form, and equality of elements is - decied by the equality of their normal forms. - - Certain Coxeter systems are *FC-finite*, meaning they contain finitely many - fully commutative elements. The seven FC-finite Coxeter group families are - (non-affine) `A_n`, `B_n = C_n`, `D_n`, `E_n`, `F_n`, `H_n` and `I_2(m)`. + Coxeter systems with finitely many FC elements, or *FC-finite* Coxeter + systems, are classfied by Stembridge in [Ste1996]_. They fall into seven + families, namely the groups of types $A_n, B_n, D_n, E_n, F_n, H_n$ and + $I_2(m)$. INPUT: - - ``data`` -- either data describing a Cartan type, or a CoxeterMatrix. Same - datum as the :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup` - constructor. + - ``data`` -- Coxeter matrix or data describing the Cartan type; the + latter should be formatted in the same way as required by the + CoxeterGroup constructor from + :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup`. OUTPUT: - The class of reduced words of fully commutative elements in the Coxeter - group described by ``data``. This will belong to either the category of - infinite enumerated sets or finite enumerated sets depending on if the group - is FC-finite. + The class of fully commutative elements in the Coxeter group constructed + from ``data``. This will belong to either the category of infinite + enumerated sets or finite enumerated sets depending on if the group is + FC-finite. EXAMPLES: - Enumerate the reduced words of fully commutative elements in `A_3` :: + Enumerate the FC elements in `A_3` in their Cartier--Foata forms :: - sage: FC = FullyCommutativeElements(['A', 3]) - sage: FC.category() + sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3.category() Category of finite enumerated sets - sage: FC.list() + sage: FCA3.list() [[], [1], [2], @@ -773,31 +763,30 @@ class FullyCommutativeElements(Parent): Count the FC elements in `B_8` :: - sage: FC = FullyCommutativeElements(['B', 8]) - sage: len(FC) # long time (7 seconds) + sage: FCB8 = FullyCommutativeElements(['B', 8]) + sage: len(FCB8) # long time (7 seconds) 14299 - Iterate through the fully commutative elements of length up to 3 in the non - FC-finite group affine `A_2` :: + Iterate through the FC elements of length up to 3 in the non FC-finite + group affine `A_2` :: - sage: FC = FullyCommutativeElements(['A', 2, 1]) - sage: FC.category() + sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) + sage: FCAffineA2.category() Category of infinite enumerated sets - sage: list(FC.iterate_to_length(2)) + sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] Constructing an element that is not fully commutative throws an error :: - sage: FC = FullyCommutativeElements(['A', 3]) - sage: FC([1,2,1]) - Traceback (most recent call last): + sage: FCA3([1,2,1]) + ValueError Traceback (most recent call last) ... - ValueError: list does not represent a fully commutative word. + ValueError: The input is not a reduced word of a fully commutative + elements. Elements are normalized to Cartier--Foata normal form upon construction :: - sage: FC = FullyCommutativeElements(['A', 3]) - sage: FC([2, 3, 1, 2]) + sage: FCA3([2, 3, 1, 2]) [2, 1, 3, 2] """ @@ -840,7 +829,7 @@ def coxeter_matrix(self): def index_set(self): r""" - Obtain the index set of the generators / simple reflections of the + Obtain the set of the generators / simple reflections of the associated Coxeter system. OUTPUT: iterable of integers @@ -859,9 +848,8 @@ def __iter__(self): letters = self.index_set() # In the following, we use a dictionary's keys as a replacement for a - # set. this is because dictinary keys are guaranteed be ordered in - # Python 3.7+, so this is an easy way to make this iterator - # deterministic. + # set. Dictinary keys are guaranteed to be ordered in Python 3.7+, so + # this is an easy way to make this iterator deterministic. recent_words = {empty_word: True} yield empty_word From f51ce23ca9e0e9b799eb9398c449c84231ef8bcc Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 15:02:01 -0600 Subject: [PATCH 109/379] more docstring edits --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index ff42a6b8e7a..05ea650cdec 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -182,7 +182,7 @@ def cartier_foata_form(self): for s in fronts: self.remove(s) - self._set_list(out_word) + return self._set_list(out_word) # Operations on fully commutative elements: From de1d2b88dbee309539bdc1633a0045c482eb6a57 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 17:49:32 -0600 Subject: [PATCH 110/379] add star operation reference --- src/doc/en/reference/references/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 5c6c0536695..ea0b248fbb2 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3794,6 +3794,10 @@ REFERENCES: No. 2001.02.069 (2002). http://www.eg-models.de/models/Classical_Models/2001.02.069/_direct_link.html +.. [Lus1985] George Lusztig, *Cells in affine Weyl groups*, Algebraic Groups + and Related Topics, Advanced Studies in Pure mathematics 6, 1985, + pp. 255-287. + .. [Lut2005] Frank H. Lutz, "Triangulated Manifolds with Few Vertices: Combinatorial Manifolds", preprint (2005), :arxiv:`math/0506372` From 4366410b3412732a213f1f3c5d9660bcf0ead302 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 17:50:38 -0600 Subject: [PATCH 111/379] add more docstring for FCElement up to star operation orbits --- .../combinat/fully_commutative_elements.py | 588 +++++++++++------- 1 file changed, 352 insertions(+), 236 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 05ea650cdec..5d39b348edd 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -184,7 +184,16 @@ def cartier_foata_form(self): return self._set_list(out_word) - # Operations on fully commutative elements: + + ########## Descents ########## + + # The following three functions deal with descents of FC elements. + # Finding descents for FC elements is easier than doing so for general + # elements but also extremely useful: repeatedly searching for descents of + # FC elements can allow us to compute the Cartier--Foata forms and certain + # coset decompositions of FC elements. The coset decompositions will + # in turn be useful for computing so-called star operations on FC elements + # and for generating FC elements by increasing length. def find_descent(self, s, side='left'): r""" @@ -290,7 +299,7 @@ def descents(self, side='left'): .. NOTE:: See the chacterization of descents for FC elements in - `find_descent`. + :func:`find_descent`. """ view = list(self) if side == 'left' else self[::-1] @@ -301,6 +310,78 @@ def descents(self, side='left'): out.add(t) return out + # coset decomposition for FC elements + def coset_decomposition(self, J, side='left'): + r""" + Return the coset decomposition of ``self`` with repsect to the + parabolic subgroup generated by ``J``. + + INPUT: + + - ``J`` -- subset of the generating set $S$ of the Coxeter system. + + OUTPUT: + + The tuple of elements $(w_J, w^J)$ such that $w=w_J \cdot w^J$, + $w_J$ is generated by the elements in $J$, and $w^J$ has no left + descent from $J$. This tuple is unique and satisfies the equation + $l(w) = l(w_J) + l(w^J)$ where $l$ denotes Coxeter length by + general theory; see Proposition 2.4.4 of [BB2005]_. + + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default: 'left'); if the value is set to 'right', + then the function returns the tuple $(w'^J, w'_J)$ from the coset + decomposition $w = w'^J \cdot w'_J$ of $w$ with respect to $J$. + + EXAMPLES: + + sage: FC = FullyCommutativeElements(['B', 6]) + sage: w = FC([1, 6, 2, 5, 4, 6, 5]) + sage: w.coset_decompostion({1}) + ([1], [6, 2, 5, 4, 6, 5]) + sage: w.coset_decompostion({1}, side = 'right') + ([], [1, 6, 2, 5, 4, 6, 5]) + sage: w.coset_decomposition({5, 6}) + ([6, 5, 6], [1, 2, 4, 5]) + sage: w.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6, 5]) + + .. NOTE:: + + The factor $w_J$ of the coset decomposition $w = w_J \cdot w^J$ can + be obtained by greedily "pulling left descents of $w$ that are in $J$ + to the left"; see the proof of [BB2005]_. This greedy algorithm works + for all elements in Coxeter group, but it becomes especially simple + for FC elements because descents are easier to find for FC elements. + + """ + string = [] # The J-string + remaining = self.clone() # The remainder + + if side == 'right': + remaining._set_list(remaining[::-1]) + + while True: + x = next((x for x in J if remaining.has_descent(x, side='left')), None) + if x is not None: + string.append(x) + remaining.remove(x) + else: + break + + if side == 'right': + remaining._set_list(remaining[::-1]) + string = string[::-1] + + string = self.parent().element_class(self.parent(), string, check=False) + remaining.set_immutable() + + return (string, remaining) if side == 'left' else (remaining, string) + + # First application of coset decompositions: + # help enumerate FC elements of a Coxeter system by induction on length: + def still_reduced_fc_after_prepending(self, s): r""" Determine if ``self`` prepended with ``s`` is still a reduced word of @@ -347,21 +428,26 @@ def still_reduced_fc_after_prepending(self, s): is a reduced word of an FC element and $s$ is not a left descent $w$. In this case, Lemma 4.1 of [Ste1996]_ implies that $sw$ is not a reduced word of an FC element if and only if some letter in - $w$ does not commute with $s$ and the following conditions - hold simultaneously for the leftmost such letter $t$: - - (1) $t$ is left descent of the word $u_1$ obtained by - removing the leftmost $s$ from $w$; - (2) $t$ is left descent of the word $u_2$ obtained by - removing the leftmost $t$ from $u_1$; - ... - (m-1) the appropriate element in $\{s, t\}$ is a left descent - of the word $u_{m-1}$ obtained by removing the leftmost letter - required to be a descent in Condition (m-2) from $u_{m-2}$. + $w$ does not commute with $s$ and for the leftmost such letter + $t$, the parabolic factor $w_J$ from the coset decomposition + $w = w_J \cdot w^J$ of $w$ with respect to $J := \{s, t\}$ + is the element $...tst$ of length $m(s,t)-1$. + + # and the following conditions + # hold simultaneously for the leftmost such letter $t$: + + # (1) $t$ is left descent of the word $u_1$ obtained by + # removing the leftmost $s$ from $w$; + # (2) $t$ is left descent of the word $u_2$ obtained by + # removing the leftmost $t$ from $u_1$; + # ... + # (m-1) the appropriate element in $\{s, t\}$ is a left descent + # of the word $u_{m-1}$ obtained by removing the leftmost letter + # required to be a descent in Condition (m-2) from $u_{m-2}$. - In the last example above, we have $s=5$, $t=4$, Condition (1) - holds, but Condition (2) fails, therefore $5w$ is still a - reduced word of an FC element. + # In the last example above, we have $s=5$, $t=4$, Condition (1) + # holds, but Condition (2) fails, therefore $5w$ is still a + # reduced word of an FC element. REFERENCES: @@ -378,17 +464,247 @@ def still_reduced_fc_after_prepending(self, s): except StopIteration: return True - u = self.clone() - u._set_list(self[j:]) - for c in range(m[s, t] - 1): - letter = t if c % 2 == 0 else s - i = u.find_descent(letter) - if i is not None: - u.pop(i) - else: - return True + + u, v = self.coset_decomposition({s, t}) + return len(u) != m[s,t]-1 + + # u = self.clone() + # u._set_list(self[j:]) + # for c in range(m[s, t] - 1): + # letter = t if c % 2 == 0 else s + # i = u.find_descent(letter) + # if i is not None: + # u.pop(i) + # else: + # return True + + # return False + + + # Second application of coset decompositions: star operations. + + # Star operations were first defined on elements of Coxeter groups by + # Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators $s,t$ + # such that $m(s,t)=3$. Later, Lusztig generalized the definition in + # [Lus1985]_, via coset decompositions, to allow star operations with + # respect to any pair of generators $s,t$ such that $m(s,t)\ge 3$. Given + # such a pair, we can potentially perform four types of star operations: + # left upper, left lower, right upper and right lower; see [Gre2006]_. + + # Template for all four types of star operations: + + def _star_operation_inner(self, J, direction, side): + assert len(J) == 2, 'J needs to contain a pair of generators.' + s, t = J + mst = self.parent().coxeter_matrix()[s,t] + # Perform the coset decomposition on the specified side: + if side == 'left': + (string, remaining) = self.coset_decomposition(J, side=side) + elif side == 'right': + (remaining, string) = self.coset_decomposition(J, side=side) + + cur_string = list(string) + + # From the coset decomposition, perform the upper or lower operation: + + # The lower star operation is defined if the parabolic factor $w_J$ + # from the coset decomposition has length at least 2; when defined, it + # removes the outmost letter in $w_J$: + if direction == 'down' and 2 <= len(string) <= mst - 1: + decrease the length of the J-string: + new_string = cur_string[1:] if side == 'left' else cur_string[:-1] + # The upper star operation is defined if the parabolic factor $w_J$ + # from the coset decomposition has length at most $m(s,t)-2$; when + # defined, it adds the appropriate letter $x$ from $J$ to $w_J$, + # extending its length by 1. + elif direction == 'up' and 1 <= len(string) <= mst - 2: + ending_letter = cur_string[0] if side == 'left' else cur_string[-1] + other = next(x for x in J if x != ending_letter) + new_string = [other] + cur_string if side == 'left' else cur_string + [other] + else: + return None + + # concatenate in the appropriate order + combined_data = new_string + list(remaining) if side == 'left' else list(remaining) + new_string + + return self.parent().element_class(self.parent(), combined_data, check=False) + + def upper_star(self, J, side='left'): + r""" + Perform an upper star operation on ``self`` with respect to the + parabolic subgroup generated by ``J``. + + INPUT: + + - ``J`` -- a set of two integers representing two non-commuting + generators of the Coxeter system. + + OUTPUT: + + The result of the star operation if it is defined on ``self``, + ``None`` otherwise. + + OPTIONAL ARGUEMNTS: + + - ``side`` -- string (default: 'left'); if set to 'right', perform a + right upper star operation. + + + EXAMPLES: + + sage: FC = FullyCommutativeElements(['B', 6]) + sage: w = FC([1, 6, 2, 5, 4, 6, 5]) + sage: w.coset_decomposition({5, 6}) + ([6, 5, 6], [1, 2, 4, 5]) + sage: w.upper_star({5,6}) + None + sage: w.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6, 5]) + sage: w.upper_star((5, 6), side='right') + [1, 6, 2, 5, 4, 6, 5 ,6] + sage: v = FC([1, 6, 2, 5, 4, 6]) + sage: v.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6]) + sage: v.upper_star({5, 6}, side='right') + [1, 6, 2, 5, 4, 6, 5] + + .. NOTE:: + As the examples illustrate, the left upper star operation is + defined if and only if in the coset decomposition $w = w_J \cdot + {}^J w$, the parabolic part has length $1\le l(w_J) \le m(s,t)-2$. + When this is the case, the operation returns $x\cdot w_J\cdot w^J$ + where $x$ is the letter $J$ different from the leftmost letter of + $w_J$. Similar facts hold for right upper star operations. See + [Gre2006]_. + """ + return self._star_operation_inner(J, 'up', side) + + def lower_star(self, J, side='left'): + + r""" + Perform a lower star operation on ``self`` with respect to the + parabolic subgroup generated by ``J``. + + INPUT: + + - ``J`` -- a set of two integers representing two non-commuting + generators of the Coxeter system. + + OUTPUT: + + The result of the star operation if it is defined on ``self``, + ``None`` otherwise. + + OPTIONAL ARGUEMNTS: + + - ``side`` -- string (default: 'left'); if set to 'right', perform a + right lower star operation. + + + EXAMPLES: + + sage: FC = FullyCommutativeElements(['B', 6]) + sage: w = FC([1, 6, 2, 5, 4, 6, 5]) + sage: w.coset_decomposition({5, 6}) + ([6, 5, 6], [1, 2, 4, 5]) + sage: w.lower_star({5,6}) + [5, 6, 1, 2, 4, 5] + sage: w.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6, 5]) + sage: w.lower_star((5, 6), side='right') + [1, 6, 2, 5, 4, 6] + sage: v = FC([1, 6, 2, 5, 4, 6]) + sage: v.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6]) + sage: v.lower_star({5, 6}, side='right') + None + + .. NOTE:: + As the examples illustrate, the left lower star operation is + defined if and only if in the coset decomposition $w = w_J \cdot + {}^J w$, the parabolic part has length $2\le l(w_J) \le m(s,t)-1$. + When this is the case, the operation removes the leftmost letter + of $w_J$ from $w$. Similar facts hold for right upper star + operations. See [Gre2006]_. + + """ + return self._star_operation_inner(J, 'down', side) + - return False + # Star operations are important to the study of Kazhdan--Lusztig cells in + # Coxeter groups; see, for example, [KL1979]_ and [GX2020]_. The orbits of + # suitable FC elements under star operations are often certain + # Kazhdan--Lusztig cells. For example, ... + + + # def star_orbit(self, side='left', **kargs): + # r""" + # Compute the star operation orbit of ``self``. + + # OUTPUT: + # The set containing all elements that can be obtained from ``self`` via + # a sequence of star operation of the specified type. + + # OPTIONAL ARGUMENTS: + + # - ``side`` -- string (default: 'left'); if set to 'right', the + # function compute the orbit for the specified type of right star + # operations. + # - ``upper_only`` -- boolean (default: False); if passed, compute only + # the set of elements that can be obtained from ``self`` via upper + # star operations on the specified side. + # - ``lower_only`` -- boolean (default: False); if passed, compute only + # the set of elements that can be obtained from ``self`` via lower + # star operations on the specified side. + + + # EXAMPLES: + + # Compute the left star closure of [1] in the group `I_8`. This should be + # set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same + # as the left *upper* star closure :: + + # sage: FC = FullyCommutativeElements(['I', 8]) + # sage: sorted(FC([1]).star_closure()) + # [[1], + # [1, 2, 1], + # [1, 2, 1, 2, 1], + # [1, 2, 1, 2, 1, 2, 1], + # [2, 1], + # [2, 1, 2, 1], + # [2, 1, 2, 1, 2, 1]] + # sage: FC([1]).star_closure() == FC([1]).star_closure(upper_only=True) + # True + # """ + # m = self.parent().coxeter_matrix() + # adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] + + # directions = {'up', 'down'} + # if 'upper_only' in kargs and kargs['upper_only']: + # directions = {'up'} + # elif 'lower_only' in kargs and kargs['lower_only']: + # directions = {'down'} + + # closure = {self} + # recent_words = {self} + # while True: + # new_words = set() + # for w in recent_words: + # for J in adjacent_pairs: + # for d in directions: + # n = w._star_operation_inner(J, d, side) + # if n is not None and n not in closure: + # new_words.add(n) + # if len(new_words) == 0: + # break + # closure.update(new_words) + # recent_words = new_words + # return closure + + + ########## Heaps ########## + # Heaps are certain posets that are very useful for representing and + # studying FC elements; see, for example, [Ste1996]_ and [GX2020]_. def heap(self, **kargs): r""" @@ -453,8 +769,8 @@ def n_value(self): Calculate the n-value of ``self``. The *n-value* of a fully commutative element is the *width* (length - of any longest antichain) of its heap poset. The n-value is important - as it coincides with Lusztig's a-value for FC elements in all Weyl and + of any longest antichain) of its heap. The n-value is important as it + coincides with Lusztig's a-value for FC elements in all Weyl and affine Weyl groups as well as so-called star-reducible groups; see [GX2020]_. @@ -474,12 +790,15 @@ def n_value(self): def plot(self): r""" - Display a semantically helpful rendering of the heap of ``self``. + Display the Hasse diagram of the heap of ``self``. + + The Hasse diagram is rendered in the lattice $S \times \N$, with every + element $i$ in the poset drawn as a point labelled by its label $s_i$. + Every point is placed in the column for its label at a certain level. + The levels start at 0 and the level k of an element $i$ is the maximal + number $k$ such that the heap contains a chain $i_0\prec i_1\prec ... + \prec i_k$ where $i_k=i$. See [Ste1996]_ and [GX2020]_. - Specifically, if ``self`` is the word `w`, render the Hasse diagram of - the heap of `w` where each vertex is labeled by `w_i` instead of `i`. - Furthermore, vertices are arranged in level sets, where the "level" of - an element is the maximum length of a chain ending in that element. OUTPUT: GraphicsObject """ @@ -512,209 +831,6 @@ def plot(self): g.axes(False) return g - def coset_decomposition(self, J, side='left'): - r""" - Perform a coset decomposition of ``self`` with repsect to the subgroup - generated by ``J``. - - If self is `w`, then this method returns a tuple `(w_J, w^J)` such that - `w_J \in \langle J \rangle`, `w^J \not\in \langle J \rangle`, and `w = - w_J w^J`. - - .. NOTE: - - If ``J`` generates a parabolic subgroup (i.e., ``J`` is a pair of - non-commuting generators), then the decomposition returned by this - function is unique. - - INPUT: - - - ``J`` -- collection of integers; the indices of the generators that - generate the subgroup with repsect to which to perform the - decomposition - - OPTIONAL ARGUMENTS: - - - ``side`` -- string (default 'left') if 'right', perform a right coset - decomposition and return the tuple `(w^J, w_J)` where `w = w^J w_J`. - - EXAMPLES: - - Do a left and right coset decomposition in `B_6` with respect to the - parabolic subgroup generated by {5, 6} :: - - sage: FC = FullyCommutativeElements(['B', 6]) - sage: x = FC([1, 6, 2, 5, 4, 6, 5]) - sage: x.coset_decomposition((5, 6)) - ([6, 5, 6], [1, 2, 4, 5]) - sage: x.coset_decomposition((5, 6), side='right') - ([1, 6, 2, 5, 4], [6, 5]) - """ - string = [] # The J-string - remaining = self.clone() # The remainder - - if side == 'right': - remaining._set_list(remaining[::-1]) - - while True: - x = next((x for x in J if remaining.has_descent(x, side='left')), None) - if x is not None: - string.append(x) - remaining.remove(x) - else: - break - - if side == 'right': - remaining._set_list(remaining[::-1]) - string = string[::-1] - - string = self.parent().element_class(self.parent(), string, check=False) - remaining.set_immutable() - - return (string, remaining) if side == 'left' else (remaining, string) - - def _star_operation_inner(self, J, direction, side): - mst = self.parent().coxeter_matrix()[J[0], J[1]] - # Decompose and assign the appropriate names based on the side of the decomposition. - if side == 'left': - (string, remaining) = self.coset_decomposition(J, side=side) - elif side == 'right': - (remaining, string) = self.coset_decomposition(J, side=side) - - cur_string = list(string) - - if direction == 'down' and 2 <= len(string) <= mst - 1: - # Decrease the length of the J-string - new_string = cur_string[1:] if side == 'left' else cur_string[:-1] - elif direction == 'up' and 1 <= len(string) <= mst - 2: - # Increase the length of the J-string - ending_letter = cur_string[0] if side == 'left' else cur_string[-1] - other = next(x for x in J if x != ending_letter) - new_string = [other] + cur_string if side == 'left' else cur_string + [other] - else: - return None - - # concatenate in the appropriate order - combined_data = new_string + list(remaining) if side == 'left' else list(remaining) + new_string - - return self.parent().element_class(self.parent(), combined_data, check=False) - - def upper_star(self, J, side='left'): - r""" - Perform an upper star operation on ``self`` with respect to the - parabolic subgroup generated by ``J``. - - If ``self`` is `w` and `w` has the (unique) coset decomposition `w_J - w^J` (where `w_J` is necessarily an alternating string in `\{s, t\}` - where `J = \{s, t\}`), an upper star operation on `w` increases the - length of `w_J` by adding element on the left (if such an addition would - not result in a long braid), and a lower star operation decreases the - length of `w_J` by removing the left-most element (if such an removal - would not result in `w_J` being empty). - - INPUT: - - - ``J`` -- tuple of integers; a *pair* of indices of *non-commuting* - generators, such that ``J`` generates a parabolic subgroup. - - OPTIONAL ARGUEMNTS: - - - ``side`` -- string (default 'left') if 'right', perform a right upper - star operation. - - OUTPUT: - - The result of the star operation if it is defined on ``self``, otherwise - ``None``. - - EXAMPLES: - - Perform right star operations on an element in `B_6`, with repsect to - the parabolic subgroup `\langle 5, 6 \rangle` (the long braid pair). Here both - star operations are defined :: - - sage: FC = FullyCommutativeElements(['B', 6]) - sage: x = FC([1, 6, 2, 5, 4, 6, 5]) - sage: x.upper_star((5, 6), side='right') - [1, 6, 2, 5, 4, 6, 5, 6] - sage: x.lower_star((5, 6), side='right') - [1, 6, 2, 5, 4, 6] - - For the same element, a left upper star operation is not defined :: - - sage: FC = FullyCommutativeElements(['B', 6]) - sage: x = FC([1, 6, 2, 5, 4, 6, 5]) - sage: x.upper_star((5, 6)) is None - True - """ - return self._star_operation_inner(J, 'up', side) - - def lower_star(self, J, side='left'): - r""" - Perform a lower star operation on ``self``; see ``.upper_star`` for - definitions and documentation. - """ - return self._star_operation_inner(J, 'down', side) - - def star_closure(self, side='left', **kargs): - r""" - Compute the star operation closure of ``self``. - - OPTIONAL ARGUMENTS: - - - ``side`` -- string (default 'left') if 'right', compute the right star - closure. - - ``upper_only`` -- boolean (default False) if passed, compute the upper - star closure. - - ``lower_only`` -- boolean (default False) if passed, compute the lower - star closure. - - OUTPUT: set; the (upper, lower, or both) star closure of ``self``. - - EXAMPLES: - - Compute the left star closure of [1] in the group `I_8`. This should be - set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same - as the left *upper* star closure :: - - sage: FC = FullyCommutativeElements(['I', 8]) - sage: sorted(FC([1]).star_closure()) - [[1], - [1, 2, 1], - [1, 2, 1, 2, 1], - [1, 2, 1, 2, 1, 2, 1], - [2, 1], - [2, 1, 2, 1], - [2, 1, 2, 1, 2, 1]] - sage: FC([1]).star_closure() == FC([1]).star_closure(upper_only=True) - True - """ - m = self.parent().coxeter_matrix() - adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] - - directions = {'up', 'down'} - if 'upper_only' in kargs and kargs['upper_only']: - directions = {'up'} - elif 'lower_only' in kargs and kargs['lower_only']: - directions = {'down'} - - closure = {self} - recent_words = {self} - while True: - new_words = set() - for w in recent_words: - for J in adjacent_pairs: - for d in directions: - n = w._star_operation_inner(J, d, side) - if n is not None and n not in closure: - new_words.add(n) - if len(new_words) == 0: - break - closure.update(new_words) - recent_words = new_words - return closure - - class FullyCommutativeElements(Parent): r""" Class for the set of fully commutative (FC) elements of a Coxeter systems From deb33bfb4ec9845b93eacc0a67a8a07a8fd48e56 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sat, 11 Jul 2020 01:31:19 -0600 Subject: [PATCH 112/379] Have documentation link go directly to class instead of file. --- src/sage/combinat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/__init__.py b/src/sage/combinat/__init__.py index ac8f9b4a304..d7d8e22b536 100644 --- a/src/sage/combinat/__init__.py +++ b/src/sage/combinat/__init__.py @@ -18,7 +18,7 @@ - :ref:`sage.combinat.crystals` - :ref:`sage.combinat.root_system` - :ref:`sage.combinat.sf` - - :ref:`sage.combinat.fully_commutative_elements` + - :class:`~sage.combinat.fully_commutative_elements.FullyCommutativeElements` - :ref:`sage.combinat.counting` - :ref:`sage.combinat.enumerated_sets` From 1ac53a2cef081557c7c60b2c2449115bc153748c Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sat, 11 Jul 2020 18:45:26 -0600 Subject: [PATCH 113/379] Fix typos, errors, examples, non-serious test failures. --- .../combinat/fully_commutative_elements.py | 105 +++++++++--------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 5d39b348edd..7f74640db0e 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -57,19 +57,19 @@ def check(self): sage: FC([]) [] sage: FC([1,2]) - [1,2] + [1, 2] sage: FC([2,3,2]) - [2,3,2] + [2, 3, 2] sage: FC([3,2,3]) - [3,2,3] + [3, 2, 3] The output is the normalized form of ``self``, which may be a different reduced word of the element represented by the input :: sage: FC([3,1]) - [1,3] + [1, 3] sage: FC([2,3,1]) - [2,1,3] + [2, 1, 3] sage: FC([1,3]) == FC([3,1]) True @@ -77,29 +77,26 @@ def check(self): ValueEror :: sage: FC([1,2,1]) - ValueError Traceback (most recent call last) + Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative - elements. + ValueError: The input is not a reduced word of a fully commutative element. sage: FC([2,3,2,3]) - ValueError Traceback (most recent call last) + Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative - elements. + ValueError: The input is not a reduced word of a fully commutative element. .. SEEALSO:: :func:`_is_fully_commutative` :func:`normalize` """ - if not self._is_fully_commutative(self._get_list()): - raise ValueError('The input is not a reduced word of a fully commutative element.') + return self._is_fully_commutative() def normalize(self): r""" Normalize ``self`` to the Cartier--Foata normal form. """ - return cartier_foata_form(self) + return self._cartier_foata_form() def _is_fully_commutative(self): @@ -108,7 +105,7 @@ def _is_fully_commutative(self): criterion. """ matrix = self.parent().coxeter_matrix() - w = self + w = tuple(self) def contains_long_braid(w): for i in range(0, len(w)-2): @@ -116,7 +113,7 @@ def contains_long_braid(w): b = w[i+1] m = matrix[a, b] if m > 2 and i+m <= len(w): - ab_braid = [a, b] * (m // 2) + ([a,] if m % 2 == 1 else []) + ab_braid = (a, b) * (m // 2) + ((a,) if m % 2 == 1 else ()) if w[i:i+m] == ab_braid: return True return False @@ -124,8 +121,9 @@ def contains_long_braid(w): def commute_once(word, i): return word[:i] + (word[i+1], word[i]) + word[i+2:] + not_fc = ValueError('The input is not a reduced word of a fully commutative element.') if contains_long_braid(w): - return False + raise not_fc else: l, checked, queue = len(w), {w}, deque([w]) while queue: @@ -136,14 +134,14 @@ def commute_once(word, i): new_word = commute_once(word, i) if new_word not in checked: if contains_long_braid(new_word): - return False + raise not_fc else: checked.add(new_word) queue.appendleft(new_word) return True - def cartier_foata_form(self): + def _cartier_foata_form(self): r""" Return the Cartier--Foata form of ``self``. @@ -153,11 +151,11 @@ def cartier_foata_form(self): The following reduced words express the same FC elements in `B_5` :: sage: FC = FullyCommutativeElements(['B', 5]) - sage: FC([1, 4, 3, 5, 2, 4, 3]).cartier_foata_form() + sage: FC([1, 4, 3, 5, 2, 4, 3]) [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 1, 3, 5, 2, 4, 3]).cartier_foata_form() + sage: FC([4, 1, 3, 5, 2, 4, 3]) [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 3, 1, 5, 4, 2, 3]).cartier_foata_form() + sage: FC([4, 3, 1, 5, 4, 2, 3]) [1, 4, 3, 5, 2, 4, 3] .. NOTE:: @@ -182,7 +180,7 @@ def cartier_foata_form(self): for s in fronts: self.remove(s) - return self._set_list(out_word) + self._set_list(out_word) ########## Descents ########## @@ -224,15 +222,13 @@ def find_descent(self, s, side='left'): sage: w.find_descent(1) 0 sage: w.find_descent(1, side='right') - None + sage: w.find_descent(4) 1 sage: w.find_descent(4, side='right') - None + sage: w.find_descent(3) - None - sage: w.find_descent(4, side='right') - 6 + .. NOTE:: A generator $s$ is a left descent of an FC element $w$ if @@ -338,10 +334,10 @@ def coset_decomposition(self, J, side='left'): sage: FC = FullyCommutativeElements(['B', 6]) sage: w = FC([1, 6, 2, 5, 4, 6, 5]) - sage: w.coset_decompostion({1}) + sage: w.coset_decomposition({1}) ([1], [6, 2, 5, 4, 6, 5]) - sage: w.coset_decompostion({1}, side = 'right') - ([], [1, 6, 2, 5, 4, 6, 5]) + sage: w.coset_decomposition({1}, side = 'right') + ([1, 6, 2, 5, 4, 6, 5], []) sage: w.coset_decomposition({5, 6}) ([6, 5, 6], [1, 2, 4, 5]) sage: w.coset_decomposition({5, 6}, side='right') @@ -407,7 +403,7 @@ def still_reduced_fc_after_prepending(self, s): False sage: w.still_reduced_fc_after_prepending(3) True - sage: u = FC([3,1,2]) + sage: u = FCB3([3,1,2]) sage: u.still_reduced_fc_after_prepending(1) False sage: u.still_reduced_fc_after_prepending(2) @@ -433,21 +429,21 @@ def still_reduced_fc_after_prepending(self, s): $w = w_J \cdot w^J$ of $w$ with respect to $J := \{s, t\}$ is the element $...tst$ of length $m(s,t)-1$. - # and the following conditions - # hold simultaneously for the leftmost such letter $t$: - - # (1) $t$ is left descent of the word $u_1$ obtained by - # removing the leftmost $s$ from $w$; - # (2) $t$ is left descent of the word $u_2$ obtained by - # removing the leftmost $t$ from $u_1$; - # ... - # (m-1) the appropriate element in $\{s, t\}$ is a left descent - # of the word $u_{m-1}$ obtained by removing the leftmost letter - # required to be a descent in Condition (m-2) from $u_{m-2}$. + and the following conditions + hold simultaneously for the leftmost such letter $t$: + + (1) $t$ is left descent of the word $u_1$ obtained by + removing the leftmost $s$ from $w$; + (2) $t$ is left descent of the word $u_2$ obtained by + removing the leftmost $t$ from $u_1$; + ... + (m-1) the appropriate element in $\{s, t\}$ is a left descent + of the word $u_{m-1}$ obtained by removing the leftmost letter + required to be a descent in Condition (m-2) from $u_{m-2}$. - # In the last example above, we have $s=5$, $t=4$, Condition (1) - # holds, but Condition (2) fails, therefore $5w$ is still a - # reduced word of an FC element. + In the last example above, we have $s=5$, $t=4$, Condition (1) + holds, but Condition (2) fails, therefore $5w$ is still a + reduced word of an FC element. REFERENCES: @@ -511,7 +507,7 @@ def _star_operation_inner(self, J, direction, side): # from the coset decomposition has length at least 2; when defined, it # removes the outmost letter in $w_J$: if direction == 'down' and 2 <= len(string) <= mst - 1: - decrease the length of the J-string: + # decrease the length of the J-string: new_string = cur_string[1:] if side == 'left' else cur_string[:-1] # The upper star operation is defined if the parabolic factor $w_J$ # from the coset decomposition has length at most $m(s,t)-2$; when @@ -557,11 +553,11 @@ def upper_star(self, J, side='left'): sage: w.coset_decomposition({5, 6}) ([6, 5, 6], [1, 2, 4, 5]) sage: w.upper_star({5,6}) - None + sage: w.coset_decomposition({5, 6}, side='right') ([1, 6, 2, 5, 4], [6, 5]) sage: w.upper_star((5, 6), side='right') - [1, 6, 2, 5, 4, 6, 5 ,6] + [1, 6, 2, 5, 4, 6, 5, 6] sage: v = FC([1, 6, 2, 5, 4, 6]) sage: v.coset_decomposition({5, 6}, side='right') ([1, 6, 2, 5, 4], [6]) @@ -608,7 +604,9 @@ def lower_star(self, J, side='left'): sage: w.coset_decomposition({5, 6}) ([6, 5, 6], [1, 2, 4, 5]) sage: w.lower_star({5,6}) - [5, 6, 1, 2, 4, 5] + [1, 5, 2, 4, 6, 5] + sage: w.lower_star({5,6}).coset_decomposition({5, 6}) + ([5, 6], [1, 2, 4, 5]) sage: w.coset_decomposition({5, 6}, side='right') ([1, 6, 2, 5, 4], [6, 5]) sage: w.lower_star((5, 6), side='right') @@ -617,7 +615,7 @@ def lower_star(self, J, side='left'): sage: v.coset_decomposition({5, 6}, side='right') ([1, 6, 2, 5, 4], [6]) sage: v.lower_star({5, 6}, side='right') - None + .. NOTE:: As the examples illustrate, the left lower star operation is @@ -895,10 +893,9 @@ class FullyCommutativeElements(Parent): Constructing an element that is not fully commutative throws an error :: sage: FCA3([1,2,1]) - ValueError Traceback (most recent call last) + Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative - elements. + ValueError: The input is not a reduced word of a fully commutative element. Elements are normalized to Cartier--Foata normal form upon construction :: From 6c53c043072d7e25e524e5ceb1a15e0665fb8284 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Sat, 11 Jul 2020 20:47:03 -0600 Subject: [PATCH 114/379] fix still_fc criterion --- .../combinat/fully_commutative_elements.py | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 7f74640db0e..86e1a9ad967 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -414,7 +414,7 @@ def still_reduced_fc_after_prepending(self, s): sage: FCA5 = FullyCommutativeElements(['A',5]) sage: w = FCA5([2,4,1,3,2,5]) sage: w.still_reduced_fc_after_prepending(5) - True + False .. NOTE:: @@ -424,18 +424,19 @@ def still_reduced_fc_after_prepending(self, s): is a reduced word of an FC element and $s$ is not a left descent $w$. In this case, Lemma 4.1 of [Ste1996]_ implies that $sw$ is not a reduced word of an FC element if and only if some letter in - $w$ does not commute with $s$ and for the leftmost such letter - $t$, the parabolic factor $w_J$ from the coset decomposition - $w = w_J \cdot w^J$ of $w$ with respect to $J := \{s, t\}$ - is the element $...tst$ of length $m(s,t)-1$. - - and the following conditions + $w$ does not commute with $s$ and the following conditions hold simultaneously for the leftmost such letter $t$: - (1) $t$ is left descent of the word $u_1$ obtained by - removing the leftmost $s$ from $w$; - (2) $t$ is left descent of the word $u_2$ obtained by + + (1) $t$ is left descent of the word $u_1$ obtained by removing + all letters to the left of the aforementioned $t$ from $w$; + (this condition is automatically true) + + (2) $s$ is left descent of the word $u_2$ obtained by removing the leftmost $t$ from $u_1$; + + (3) $t$ is left descent of the word $u_3$ obtained by + removing the leftmost $s$ from $u_2$; ... (m-1) the appropriate element in $\{s, t\}$ is a left descent of the word $u_{m-1}$ obtained by removing the leftmost letter @@ -445,6 +446,12 @@ def still_reduced_fc_after_prepending(self, s): holds, but Condition (2) fails, therefore $5w$ is still a reduced word of an FC element. + Note that the conditions (1)--(m-1) are equivalent to the + condition that the parabolic factor $u_J$ from the coset + decomposition $u_1 = u_J \cdot u^J$ of $u_1$ with respect to + $J := \{s, t\}$ is the element $tst...$ of length $m(s,t)-1$. + + REFERENCES: See Lemma 4.1 of [Ste1996]_. @@ -461,8 +468,10 @@ def still_reduced_fc_after_prepending(self, s): return True - u, v = self.coset_decomposition({s, t}) - return len(u) != m[s,t]-1 + u = self.clone() + u._set_list(self[j:]) + x, y= u.coset_decomposition({s, t}) + return len(x) != m[s,t]-1 # u = self.clone() # u._set_list(self[j:]) From 95f04fa3f44288dabad263e7b74be4857fa0e299 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sun, 12 Jul 2020 12:44:40 -0600 Subject: [PATCH 115/379] Clean up comments and docs; run formatters --- .../combinat/fully_commutative_elements.py | 478 +++++++++--------- 1 file changed, 232 insertions(+), 246 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 86e1a9ad967..2162c3a2039 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -1,10 +1,10 @@ r""" Fully commutative elements of Coxeter groups -An element $w$ in a Coxeter system (W,S) is fully commutative (FC) if +An element `w` in a Coxeter system (W,S) is fully commutative (FC) if every two reduced word of w can be related by a sequence of only -commutation relations, i.e., relations of the form $st=ts$ where $s,t$ are -commuting generators in $S$. See [Ste1996]_. +commutation relations, i.e., relations of the form `st=ts` where `s,t` are +commuting generators in `S`. See [Ste1996]_. """ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -21,16 +21,16 @@ class FullyCommutativeElement(NormalizedClonableList): r""" A (reduced word of a) fully commutative (FC) element in a Coxeter system. - An element $w$ in a Coxeter system (W,S) is fully commutative (FC) if - every two reduced word of w can be related by a sequence of only - commutation relations, i.e., relations of the form $st=ts$ where $s,t$ are - commuting generators in $S$. Equivalently, $w$ is FC if and only if for - every pair of generators $s,t \in S$ for which $m(s,t)>2$, no reduced word - of $w$ contains the "braid" word $sts...$ of length $m(s,t)$ as a contiguous - subword. See [Ste1996]_. We will use the braid-avoidance criterion to - check if an element is FC. + An element `w` in a Coxeter system (W,S) is fully commutative (FC) if every + two reduced word of w can be related by a sequence of only commutation + relations, i.e., relations of the form `st=ts` where `s,t` are commuting + generators in `S`. Equivalently, `w` is FC if and only if for every pair of + generators `s,t \in S` for which `m(s,t)>2`, no reduced word of `w` contains + the "braid" word `sts...` of length `m(s,t)` as a contiguous subword. See + [Ste1996]_. We will use the braid-avoidance criterion to check if an element + is FC. - Every FC element has a canonical reduced word called its Cartier--Foata + Every FC element has a canonical reduced word called its Cartier--Foata form. See [Gre2006]_. We will normalize each FC element to this form. """ @@ -38,7 +38,25 @@ class FullyCommutativeElement(NormalizedClonableList): # Methods required as a subclass of NormalizedClonableList: def check(self): r""" - Check if ``self`` is the reduced word of an FC element. + Called automatically when an element is created. Alias of + :func:`is_fully_commutative` + """ + return self.is_fully_commutative() + + def normalize(self): + r""" + Called automatically when an element is created. Alias of + :func:`cartier_foata_form` + """ + return self.cartier_foata_form() + + + def is_fully_commutative(self): + r""" + Check if ``self`` is the reduced word of an FC element. + + :func:`check` is an alias of this method, and is called automatically + when an element is created. EXAMPLES: @@ -63,8 +81,8 @@ def check(self): sage: FC([3,2,3]) [3, 2, 3] - The output is the normalized form of ``self``, which may be a - different reduced word of the element represented by the input :: + The output is the normalized form of ``self``, which may be a different + reduced word of the element represented by the input :: sage: FC([3,1]) [1, 3] @@ -72,7 +90,7 @@ def check(self): [2, 1, 3] sage: FC([1,3]) == FC([3,1]) True - + If the input is not the reduced word of an FC element, return a ValueEror :: @@ -84,25 +102,7 @@ def check(self): sage: FC([2,3,2,3]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative element. - - .. SEEALSO:: - :func:`_is_fully_commutative` - :func:`normalize` - """ - return self._is_fully_commutative() - - def normalize(self): - r""" - Normalize ``self`` to the Cartier--Foata normal form. - """ - return self._cartier_foata_form() - - - def _is_fully_commutative(self): - r""" - Determine if ``self`` represents an FC element via the braid-avoidance - criterion. + ValueError: The input is not a reduced word of a fully commutative element. """ matrix = self.parent().coxeter_matrix() w = tuple(self) @@ -141,10 +141,12 @@ def commute_once(word, i): return True - def _cartier_foata_form(self): + def cartier_foata_form(self): r""" Return the Cartier--Foata form of ``self``. + :func:`normalize` is an alias of this method, and is called + automatically when an element is created. EXAMPLES: @@ -157,18 +159,18 @@ def _cartier_foata_form(self): [1, 4, 3, 5, 2, 4, 3] sage: FC([4, 3, 1, 5, 4, 2, 3]) [1, 4, 3, 5, 2, 4, 3] - + .. NOTE:: - The Cartier--Foata form of a reduced word of an FC element w can be - found recursively by repeatedly moving left descents of elements - to the left and ordering the left descents from small to large. In - the above example, the left descents of the element are 4 and 1, - therefore the Cartier--Foata form of the element is the - concatenation of [1,4] with the Cartier--Foata form of the - remaining part of the word. See [Gre2006]_. - - .. SEEALSO:: - :func:`descents` + + The Cartier--Foata form of a reduced word of an FC element w + can be found recursively by repeatedly moving left descents of + elements to the left and ordering the left descents from small to + large. In the above example, the left descents of the element are 4 + and 1, therefore the Cartier--Foata form of the element is the + concatenation of [1,4] with the Cartier--Foata form of the remaining + part of the word. See [Gre2006]_. + + .. SEEALSO:: :func:`descents` """ self._require_mutable() @@ -209,10 +211,9 @@ def find_descent(self, s, side='left'): OPTIONAL ARGUMENTS: - - ``side`` -- string (default: 'left'); if the argument is set - to 'right', the function checks if ``s`` is a right descent of - ``self`` and finds the index of the rightmost occurrence of ``s`` - if so. + - ``side`` -- string (default: 'left'); if the argument is set to + 'right', the function checks if ``s`` is a right descent of ``self`` + and finds the index of the rightmost occurrence of ``s`` if so. EXAMPLES:: @@ -230,11 +231,12 @@ def find_descent(self, s, side='left'): sage: w.find_descent(3) - .. NOTE:: - A generator $s$ is a left descent of an FC element $w$ if - and only if for one (equivalently, every) reduced word of $w$, $s$ + .. NOTE:: + + A generator `s` is a left descent of an FC element `w` if and + only if for one (equivalently, every) reduced word of `w`, `s` appears to in the word and every generator to the left of the - leftmost $s$ in the word commutes with $s$. A similar result holds + leftmost `s` in the word commutes with `s`. A similar result holds for right descents of FC elements. """ @@ -253,8 +255,8 @@ def has_descent(self, s, side='left'): OPTIONAL ARGUMENTS: - - ``side`` -- string (default: 'left'); if set to 'right', - determine if ``self`` has ``s`` as a right descent. + - ``side`` -- string (default: 'left'); if set to 'right', determine if + ``self`` has ``s`` as a right descent. EXAMPLES:: @@ -269,8 +271,7 @@ def has_descent(self, s, side='left'): sage: w.has_descent(4, side='right') False - .. SEEALSO:: - :func:`find_descent` + .. SEEALSO:: :func:`find_descent` """ return self.find_descent(s, side=side) is not None @@ -280,16 +281,16 @@ def descents(self, side='left'): OPTIONAL ARGUMENTS: - - ``side`` -- string (default: 'left'); if set to 'right', - find the right descents. + - ``side`` -- string (default: 'left'); if set to 'right', find the + right descents. EXAMPLES:: sage: FC = FullyCommutativeElements(['B', 5]) sage: w = FC([1, 4, 3, 5, 2, 4, 3]) - sage: w.descents() - {1, 4} + sage: sorted(w.descents()) + [1, 4] sage: w.descents(side='right') {3} @@ -309,28 +310,28 @@ def descents(self, side='left'): # coset decomposition for FC elements def coset_decomposition(self, J, side='left'): r""" - Return the coset decomposition of ``self`` with repsect to the - parabolic subgroup generated by ``J``. - + Return the coset decomposition of ``self`` with repsect to the parabolic + subgroup generated by ``J``. + INPUT: - - ``J`` -- subset of the generating set $S$ of the Coxeter system. + - ``J`` -- subset of the generating set `S` of the Coxeter system. OUTPUT: - The tuple of elements $(w_J, w^J)$ such that $w=w_J \cdot w^J$, - $w_J$ is generated by the elements in $J$, and $w^J$ has no left - descent from $J$. This tuple is unique and satisfies the equation - $l(w) = l(w_J) + l(w^J)$ where $l$ denotes Coxeter length by - general theory; see Proposition 2.4.4 of [BB2005]_. + The tuple of elements `(w_J, w^J)` such that `w=w_J \cdot w^J`, `w_J` is + generated by the elements in `J`, and `w^J` has no left descent from + `J`. This tuple is unique and satisfies the equation `l(w) = l(w_J) + + l(w^J)` where `l` denotes Coxeter length by general theory; see + Proposition 2.4.4 of [BB2005]_. OPTIONAL ARGUMENTS: - ``side`` -- string (default: 'left'); if the value is set to 'right', - then the function returns the tuple $(w'^J, w'_J)$ from the coset - decomposition $w = w'^J \cdot w'_J$ of $w$ with respect to $J$. + then the function returns the tuple `(w'^J, w'_J)` from the coset + decomposition `w = w'^J \cdot w'_J` of `w` with respect to `J`. - EXAMPLES: + EXAMPLES:: sage: FC = FullyCommutativeElements(['B', 6]) sage: w = FC([1, 6, 2, 5, 4, 6, 5]) @@ -345,12 +346,13 @@ def coset_decomposition(self, J, side='left'): .. NOTE:: - The factor $w_J$ of the coset decomposition $w = w_J \cdot w^J$ can - be obtained by greedily "pulling left descents of $w$ that are in $J$ - to the left"; see the proof of [BB2005]_. This greedy algorithm works - for all elements in Coxeter group, but it becomes especially simple - for FC elements because descents are easier to find for FC elements. - + The factor `w_J` of the coset decomposition `w = w_J \cdot + w^J` can be obtained by greedily "pulling left descents of `w` that + are in `J` to the left"; see the proof of [BB2005]_. This greedy + algorithm works for all elements in Coxeter group, but it becomes + especially simple for FC elements because descents are easier to + find for FC elements. + """ string = [] # The J-string remaining = self.clone() # The remainder @@ -380,8 +382,8 @@ def coset_decomposition(self, J, side='left'): def still_reduced_fc_after_prepending(self, s): r""" - Determine if ``self`` prepended with ``s`` is still a reduced word of - an FC element in the Coxeter system. + Determine if ``self`` prepended with ``s`` is still a reduced word of an + FC element in the Coxeter system. INPUT: @@ -417,39 +419,40 @@ def still_reduced_fc_after_prepending(self, s): False - .. NOTE:: - If $w$ is a reduced word of an element, then the concatenation - $sw$ is still a reduced word if and only if $s$ is not a left - descent of $w$ by general Coxeter group theory. So now assume $w$ - is a reduced word of an FC element and $s$ is not a left descent - $w$. In this case, Lemma 4.1 of [Ste1996]_ implies that $sw$ is - not a reduced word of an FC element if and only if some letter in - $w$ does not commute with $s$ and the following conditions - hold simultaneously for the leftmost such letter $t$: - - - (1) $t$ is left descent of the word $u_1$ obtained by removing - all letters to the left of the aforementioned $t$ from $w$; - (this condition is automatically true) - - (2) $s$ is left descent of the word $u_2$ obtained by - removing the leftmost $t$ from $u_1$; - - (3) $t$ is left descent of the word $u_3$ obtained by - removing the leftmost $s$ from $u_2$; - ... - (m-1) the appropriate element in $\{s, t\}$ is a left descent - of the word $u_{m-1}$ obtained by removing the leftmost letter - required to be a descent in Condition (m-2) from $u_{m-2}$. - - In the last example above, we have $s=5$, $t=4$, Condition (1) - holds, but Condition (2) fails, therefore $5w$ is still a + .. NOTE:: + + If `w` is a reduced word of an element, then the concatenation + `sw` is still a reduced word if and only if `s` is not a left + descent of `w` by general Coxeter group theory. So now assume `w` is + a reduced word of an FC element and `s` is not a left descent `w`. + In this case, Lemma 4.1 of [Ste1996]_ implies that `sw` is not a + reduced word of an FC element if and only if some letter in `w` does + not commute with `s` and the following conditions hold + simultaneously for the leftmost such letter `t`: + + + (1) `t` is left descent of the word `u_1` obtained by removing + all letters to the left of the aforementioned `t` from `w`; + (this condition is automatically true) + + (2) `s` is left descent of the word `u_2` obtained by + removing the leftmost `t` from `u_1`; + + (3) `t` is left descent of the word `u_3` obtained by + removing the leftmost `s` from `u_2`; + ... + (m-1) the appropriate element in `\{s, t\}` is a left descent + of the word `u_{m-1}` obtained by removing the leftmost letter + required to be a descent in Condition (m-2) from `u_{m-2}`. + + In the last example above, we have `s=5`, `t=4`, Condition (1) + holds, but Condition (2) fails, therefore `5w` is still a reduced word of an FC element. Note that the conditions (1)--(m-1) are equivalent to the - condition that the parabolic factor $u_J$ from the coset - decomposition $u_1 = u_J \cdot u^J$ of $u_1$ with respect to - $J := \{s, t\}$ is the element $tst...$ of length $m(s,t)-1$. + condition that the parabolic factor `u_J` from the coset + decomposition `u_1 = u_J \cdot u^J` of `u_1` with respect to + `J := \{s, t\}` is the element `tst...` of length `m(s,t)-1`. REFERENCES: @@ -473,26 +476,14 @@ def still_reduced_fc_after_prepending(self, s): x, y= u.coset_decomposition({s, t}) return len(x) != m[s,t]-1 - # u = self.clone() - # u._set_list(self[j:]) - # for c in range(m[s, t] - 1): - # letter = t if c % 2 == 0 else s - # i = u.find_descent(letter) - # if i is not None: - # u.pop(i) - # else: - # return True - - # return False - # Second application of coset decompositions: star operations. # Star operations were first defined on elements of Coxeter groups by - # Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators $s,t$ - # such that $m(s,t)=3$. Later, Lusztig generalized the definition in + # Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators `s,t` + # such that `m(s,t)=3`. Later, Lusztig generalized the definition in # [Lus1985]_, via coset decompositions, to allow star operations with - # respect to any pair of generators $s,t$ such that $m(s,t)\ge 3$. Given + # respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given # such a pair, we can potentially perform four types of star operations: # left upper, left lower, right upper and right lower; see [Gre2006]_. @@ -512,17 +503,17 @@ def _star_operation_inner(self, J, direction, side): # From the coset decomposition, perform the upper or lower operation: - # The lower star operation is defined if the parabolic factor $w_J$ - # from the coset decomposition has length at least 2; when defined, it - # removes the outmost letter in $w_J$: if direction == 'down' and 2 <= len(string) <= mst - 1: + # The lower star operation is defined if the parabolic factor `w_J` + # from the coset decomposition has length at least 2; when defined, it + # removes the outmost letter in `w_J`: # decrease the length of the J-string: new_string = cur_string[1:] if side == 'left' else cur_string[:-1] - # The upper star operation is defined if the parabolic factor $w_J$ - # from the coset decomposition has length at most $m(s,t)-2$; when - # defined, it adds the appropriate letter $x$ from $J$ to $w_J$, - # extending its length by 1. elif direction == 'up' and 1 <= len(string) <= mst - 2: + # The upper star operation is defined if the parabolic factor `w_J` + # from the coset decomposition has length at most `m(s,t)-2`; when + # defined, it adds the appropriate letter `x` from `J` to `w_J`, + # extending its length by 1. ending_letter = cur_string[0] if side == 'left' else cur_string[-1] other = next(x for x in J if x != ending_letter) new_string = [other] + cur_string if side == 'left' else cur_string + [other] @@ -546,8 +537,8 @@ def upper_star(self, J, side='left'): OUTPUT: - The result of the star operation if it is defined on ``self``, - ``None`` otherwise. + The result of the star operation if it is defined on ``self``, ``None`` + otherwise. OPTIONAL ARGUEMNTS: @@ -555,7 +546,7 @@ def upper_star(self, J, side='left'): right upper star operation. - EXAMPLES: + EXAMPLES:: sage: FC = FullyCommutativeElements(['B', 6]) sage: w = FC([1, 6, 2, 5, 4, 6, 5]) @@ -573,22 +564,22 @@ def upper_star(self, J, side='left'): sage: v.upper_star({5, 6}, side='right') [1, 6, 2, 5, 4, 6, 5] - .. NOTE:: + .. NOTE:: + As the examples illustrate, the left upper star operation is - defined if and only if in the coset decomposition $w = w_J \cdot - {}^J w$, the parabolic part has length $1\le l(w_J) \le m(s,t)-2$. - When this is the case, the operation returns $x\cdot w_J\cdot w^J$ - where $x$ is the letter $J$ different from the leftmost letter of - $w_J$. Similar facts hold for right upper star operations. See + defined if and only if in the coset decomposition `w = w_J \cdot + {}^J w`, the parabolic part has length `1\le l(w_J) \le m(s,t)-2`. + When this is the case, the operation returns `x\cdot w_J\cdot w^J` + where `x` is the letter `J` different from the leftmost letter of + `w_J`. Similar facts hold for right upper star operations. See [Gre2006]_. """ return self._star_operation_inner(J, 'up', side) def lower_star(self, J, side='left'): - r""" - Perform a lower star operation on ``self`` with respect to the - parabolic subgroup generated by ``J``. + Perform a lower star operation on ``self`` with respect to the parabolic + subgroup generated by ``J``. INPUT: @@ -597,8 +588,8 @@ def lower_star(self, J, side='left'): OUTPUT: - The result of the star operation if it is defined on ``self``, - ``None`` otherwise. + The result of the star operation if it is defined on ``self``, ``None`` + otherwise. OPTIONAL ARGUEMNTS: @@ -606,7 +597,7 @@ def lower_star(self, J, side='left'): right lower star operation. - EXAMPLES: + EXAMPLES:: sage: FC = FullyCommutativeElements(['B', 6]) sage: w = FC([1, 6, 2, 5, 4, 6, 5]) @@ -627,12 +618,13 @@ def lower_star(self, J, side='left'): .. NOTE:: + As the examples illustrate, the left lower star operation is - defined if and only if in the coset decomposition $w = w_J \cdot - {}^J w$, the parabolic part has length $2\le l(w_J) \le m(s,t)-1$. - When this is the case, the operation removes the leftmost letter - of $w_J$ from $w$. Similar facts hold for right upper star - operations. See [Gre2006]_. + defined if and only if in the coset decomposition `w = w_J \cdot + {}^J w`, the parabolic part has length `2\le l(w_J) \le m(s,t)-1`. + When this is the case, the operation removes the leftmost letter of + `w_J` from `w`. Similar facts hold for right upper star operations. + See [Gre2006]_. """ return self._star_operation_inner(J, 'down', side) @@ -644,69 +636,67 @@ def lower_star(self, J, side='left'): # Kazhdan--Lusztig cells. For example, ... - # def star_orbit(self, side='left', **kargs): - # r""" - # Compute the star operation orbit of ``self``. - - # OUTPUT: - # The set containing all elements that can be obtained from ``self`` via - # a sequence of star operation of the specified type. - - # OPTIONAL ARGUMENTS: - - # - ``side`` -- string (default: 'left'); if set to 'right', the - # function compute the orbit for the specified type of right star - # operations. - # - ``upper_only`` -- boolean (default: False); if passed, compute only - # the set of elements that can be obtained from ``self`` via upper - # star operations on the specified side. - # - ``lower_only`` -- boolean (default: False); if passed, compute only - # the set of elements that can be obtained from ``self`` via lower - # star operations on the specified side. - - - # EXAMPLES: - - # Compute the left star closure of [1] in the group `I_8`. This should be - # set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same - # as the left *upper* star closure :: - - # sage: FC = FullyCommutativeElements(['I', 8]) - # sage: sorted(FC([1]).star_closure()) - # [[1], - # [1, 2, 1], - # [1, 2, 1, 2, 1], - # [1, 2, 1, 2, 1, 2, 1], - # [2, 1], - # [2, 1, 2, 1], - # [2, 1, 2, 1, 2, 1]] - # sage: FC([1]).star_closure() == FC([1]).star_closure(upper_only=True) - # True - # """ - # m = self.parent().coxeter_matrix() - # adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] + def star_orbit(self, side='left', **kargs): + r""" + Compute the star operation orbit of ``self``. + + OUTPUT: The set containing all elements that can be obtained from + ``self`` via a sequence of star operation of the specified type. + + OPTIONAL ARGUMENTS: + + - ``side`` -- string (default: 'left'); if set to 'right', the function + compute the orbit for the specified type of right star operations. + - ``upper_only`` -- boolean (default: False); if passed, compute only + the set of elements that can be obtained from ``self`` via upper star + operations on the specified side. + - ``lower_only`` -- boolean (default: False); if passed, compute only + the set of elements that can be obtained from ``self`` via lower star + operations on the specified side. + + + EXAMPLES: + + Compute the left star closure of [1] in the group `I_8`. This should be + set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same + as the left *upper* star closure :: + + sage: FC = FullyCommutativeElements(['I', 8]) + sage: sorted(FC([1]).star_orbit()) + [[1], + [1, 2, 1], + [1, 2, 1, 2, 1], + [1, 2, 1, 2, 1, 2, 1], + [2, 1], + [2, 1, 2, 1], + [2, 1, 2, 1, 2, 1]] + sage: FC([1]).star_orbit() == FC([1]).star_orbit(upper_only=True) + True + """ + m = self.parent().coxeter_matrix() + adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] - # directions = {'up', 'down'} - # if 'upper_only' in kargs and kargs['upper_only']: - # directions = {'up'} - # elif 'lower_only' in kargs and kargs['lower_only']: - # directions = {'down'} - - # closure = {self} - # recent_words = {self} - # while True: - # new_words = set() - # for w in recent_words: - # for J in adjacent_pairs: - # for d in directions: - # n = w._star_operation_inner(J, d, side) - # if n is not None and n not in closure: - # new_words.add(n) - # if len(new_words) == 0: - # break - # closure.update(new_words) - # recent_words = new_words - # return closure + directions = {'up', 'down'} + if 'upper_only' in kargs and kargs['upper_only']: + directions = {'up'} + elif 'lower_only' in kargs and kargs['lower_only']: + directions = {'down'} + + closure = {self} + recent_words = {self} + while True: + new_words = set() + for w in recent_words: + for J in adjacent_pairs: + for d in directions: + n = w._star_operation_inner(J, d, side) + if n is not None and n not in closure: + new_words.add(n) + if len(new_words) == 0: + break + closure.update(new_words) + recent_words = new_words + return closure ########## Heaps ########## @@ -717,32 +707,26 @@ def heap(self, **kargs): r""" Create the heap poset of ``self``. - The heap of an FC element $w$ is a labeled poset that can be defined - from any reduced word of $w$. Different reduced words yield - isomorphic labeled posets, so the heap is well defined. + The heap of an FC element `w` is a labeled poset that can be defined + from any reduced word of `w`. Different reduced words yield isomorphic + labeled posets, so the heap is well defined. - Input: + INPUT: - - ``self`` -- list, a reduced word $w=s_0... s_{k-1}$ of an FC element. + - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. - OUTPUT: - A labeled poset where the underlying set is $\{0,1,...,k-1\}$ and - where each element $i$ carries $s_i$ as its label. + OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` + and where each element `i` carries `s_i` as its label. OPTIONAL ARGUMENTS: - - ``one_index`` -- boolean (default: False). Setting the value - to True will change the underlying set of the poset to $\{1, 2, - \dots, n\}$. - - - ``display_labeling`` -- boolean (default: False). Setting - the value to True will display the label $s_i$ for each element $i$ - of the poset. + - ``one_index`` -- boolean (default: False). Setting the value to True + will change the underlying set of the poset to `\{1, 2, \dots, n\}`. + - ``display_labeling`` -- boolean (default: False). Setting the value to + True will display the label `s_i` for each element `i` of the poset. - EXAMPLES: - - Create the heap of a fully commutative element in `A_5` :: + EXAMPLES:: sage: FC = FullyCommutativeElements(['A', 5]) sage: FC([1, 4, 3, 5, 2, 4]).heap().cover_relations() @@ -751,8 +735,9 @@ def heap(self, **kargs): [[2, 3], [2, 4], [3, 6], [3, 5], [4, 6], [1, 5]] .. NOTE:: - The partial order in the heap is defined by declaring $i\prec j$ - if $i Date: Mon, 13 Jul 2020 13:41:16 -0600 Subject: [PATCH 116/379] reorganize code for star operations, reorder function, improve docstrings --- .../combinat/fully_commutative_elements.py | 581 +++++++++--------- 1 file changed, 293 insertions(+), 288 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 2162c3a2039..3f6eba15aee 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -21,14 +21,10 @@ class FullyCommutativeElement(NormalizedClonableList): r""" A (reduced word of a) fully commutative (FC) element in a Coxeter system. - An element `w` in a Coxeter system (W,S) is fully commutative (FC) if every - two reduced word of w can be related by a sequence of only commutation - relations, i.e., relations of the form `st=ts` where `s,t` are commuting - generators in `S`. Equivalently, `w` is FC if and only if for every pair of - generators `s,t \in S` for which `m(s,t)>2`, no reduced word of `w` contains - the "braid" word `sts...` of length `m(s,t)` as a contiguous subword. See - [Ste1996]_. We will use the braid-avoidance criterion to check if an element - is FC. + An element `w` in a Coxeter system (W,S) is fully commutative (FC) if + every two reduced word of w can be related by a sequence of only + commutation relations, i.e., relations of the form `st=ts` where `s,t` are + commuting generators in `S`. Every FC element has a canonical reduced word called its Cartier--Foata form. See [Gre2006]_. We will normalize each FC element to this form. @@ -50,11 +46,21 @@ def normalize(self): """ return self.cartier_foata_form() + ########################################################################### + # Characterization and representation of FC elements # + ########################################################################### + # Full commutativity test def is_fully_commutative(self): r""" Check if ``self`` is the reduced word of an FC element. + To check if `self` is fully commutative, we use the well-known + characterization that an element `w` in a Coxeter system `(W,S)` is FC + if and only if for every pair of generators `s,t \in S` for which + `m(s,t)>2`, no reduced word of `w` contains the 'braid' word `sts...` + of length `m(s,t)` as a contiguous subword. See [Ste1996]_. + :func:`check` is an alias of this method, and is called automatically when an element is created. @@ -97,13 +103,17 @@ def is_fully_commutative(self): sage: FC([1,2,1]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative element. + ValueError: The input is not a reduced word of a fully commutative + element. sage: FC([2,3,2,3]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative element. + ValueError: The input is not a reduced word of a fully commutative + element. + """ + matrix = self.parent().coxeter_matrix() w = tuple(self) @@ -140,10 +150,10 @@ def commute_once(word, i): queue.appendleft(new_word) return True - + # Representing FC elements: Canonical forms def cartier_foata_form(self): r""" - Return the Cartier--Foata form of ``self``. + Return the Cartier--Foata normal form of ``self``. :func:`normalize` is an alias of this method, and is called automatically when an element is created. @@ -185,19 +195,159 @@ def cartier_foata_form(self): self._set_list(out_word) - ########## Descents ########## + # Representing FC elements: Heaps + + def heap(self, **kargs): + r""" + Create the heap poset of ``self``. + + The heap of an FC element `w` is a labeled poset that can be defined + from any reduced word of `w`. Different reduced words yield isomorphic + labeled posets, so the heap is well defined. + + Heaps are very useful for visualizing and studying FC elements; see, + for example, [Ste1996]_ and [GX2020]_. + + INPUT: + + - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. + + OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` + and where each element `i` carries `s_i` as its label. + + OPTIONAL ARGUMENTS: + + - ``one_index`` -- boolean (default: False). Setting the value to True + will change the underlying set of the poset to `\{1, 2, \dots, n\}`. + + - ``display_labeling`` -- boolean (default: False). Setting the value to + True will display the label `s_i` for each element `i` of the poset. + + EXAMPLES:: + + sage: FC = FullyCommutativeElements(['A', 5]) + sage: FC([1, 4, 3, 5, 2, 4]).heap().cover_relations() + [[1, 2], [1, 3], [2, 5], [2, 4], [3, 5], [0, 4]] + sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() + [[2, 3], [2, 4], [3, 6], [3, 5], [4, 6], [1, 5]] + + .. NOTE:: + + The partial order in the heap is defined by declaring `i\prec + j` if `i= 3} + for other in neighbors: + highest_level = max((j + 1 for j in range(level_zero_index) if other in letters_at_level[j]), default=None) + if highest_level: + graphics.append(plot.line([(other, highest_level), (x, level)], color='black', zorder=0)) + + g = sum(graphics) + g.axes(False) + return g + + # An application of heaps: + def n_value(self): + r""" + Calculate the n-value of ``self``. + + The *n-value* of a fully commutative element is the *width* (length + of any longest antichain) of its heap. The n-value is important as it + coincides with Lusztig's a-value for FC elements in all Weyl and + affine Weyl groups as well as so-called star-reducible groups; see + [GX2020]_. + + EXAMPLES:: + + sage: FC = FullyCommutativeElements(['A', 5]) + sage: FC([1,3]).n_value() + 2 + sage: FC([1,2,3]).n_value() + 1 + sage: FC([1,3,2]).n_value() + 2 + sage: FC([1,3,2,5]).n_value() + 3 + + """ + return self.heap().width() + + + ########################################################################### + # Descents and coset decompositions of FC elements # + ########################################################################### + + # Descents # The following three functions deal with descents of FC elements. - # Finding descents for FC elements is easier than doing so for general - # elements but also extremely useful: repeatedly searching for descents of - # FC elements can allow us to compute the Cartier--Foata forms and certain - # coset decompositions of FC elements. The coset decompositions will - # in turn be useful for computing so-called star operations on FC elements - # and for generating FC elements by increasing length. + # Descents of FC elements are easier to find than those of general + # elements, but they are also extremely useful: repeated searching of + # descents is essential to finding Cartier Foata forms and coset + # decompositions of FC elements; see :func:`cartier_foata_form` and + # :func:`coset_decomposition`. def find_descent(self, s, side='left'): r""" - Return the index of a left descent ``s`` of ``self``. + Check if ``s`` is a descent of ``self`` and find its position if so. + + Recall that a generator `s` is called a left or right descent of an + element `w` if `l(sw)` or `l(ws)` is smaller than `l(w)`, + respectively. If `w` is FC, then `s` is a left descent of `w` if and + only if `s` appears to in the word and every generator to the left of + the leftmost `s` in the word commutes with `s`. A similar + characterization exists for right descents of FC elements. + INPUT: @@ -231,14 +381,6 @@ def find_descent(self, s, side='left'): sage: w.find_descent(3) - .. NOTE:: - - A generator `s` is a left descent of an FC element `w` if and - only if for one (equivalently, every) reduced word of `w`, `s` - appears to in the word and every generator to the left of the - leftmost `s` in the word commutes with `s`. A similar result holds - for right descents of FC elements. - """ m = self.parent().coxeter_matrix() view = list(self) if side == 'left' else self[::-1] @@ -279,6 +421,13 @@ def descents(self, side='left'): r""" Obtain the set of descents on the appropriate side of ``self``. + Recall that a generator `s` is called a left or right descent of an + element `w` if `l(sw)` or `l(ws)` is smaller than `l(w)`, + respectively. If `w` is FC, then `s` is a left descent of `w` if and + only if `s` appears to in the word and every generator to the left of + the leftmost `s` in the word commutes with `s`. A similar + characterization exists for right descents of FC elements. + OPTIONAL ARGUMENTS: - ``side`` -- string (default: 'left'); if set to 'right', find the @@ -293,11 +442,11 @@ def descents(self, side='left'): [1, 4] sage: w.descents(side='right') {3} - - .. NOTE:: - See the chacterization of descents for FC elements in - :func:`find_descent`. + sage: FC = FullyCommutativeElements(['A', 5]) + sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).descents()) + [1, 4] + .. SEEALSO:: :func:`find_descent` """ view = list(self) if side == 'left' else self[::-1] m = self.parent().coxeter_matrix() @@ -307,7 +456,7 @@ def descents(self, side='left'): out.add(t) return out - # coset decomposition for FC elements + # Coset decompositions def coset_decomposition(self, J, side='left'): r""" Return the coset decomposition of ``self`` with repsect to the parabolic @@ -319,11 +468,11 @@ def coset_decomposition(self, J, side='left'): OUTPUT: - The tuple of elements `(w_J, w^J)` such that `w=w_J \cdot w^J`, `w_J` is - generated by the elements in `J`, and `w^J` has no left descent from - `J`. This tuple is unique and satisfies the equation `l(w) = l(w_J) + - l(w^J)` where `l` denotes Coxeter length by general theory; see - Proposition 2.4.4 of [BB2005]_. + The tuple of elements `(w_J, w^J)` such that `w=w_J \cdot w^J`, `w_J` + is generated by the elements in `J`, and `w^J` has no left descent + from `J`. This tuple is unique and satisfies the equation `l(w) = + l(w_J) + l(w^J)`, where `l` denotes Coxeter length, by general theory; + see Proposition 2.4.4 of [BB2005]_. OPTIONAL ARGUMENTS: @@ -377,13 +526,17 @@ def coset_decomposition(self, J, side='left'): return (string, remaining) if side == 'left' else (remaining, string) - # First application of coset decompositions: - # help enumerate FC elements of a Coxeter system by induction on length: + ########################################################################### + # Application of coset decompositions, I: New FC elements from old # + ########################################################################### + + # The following function uses coset decompositions and will help us + # generate all FC elements in a Coxeter group by induction on length. def still_reduced_fc_after_prepending(self, s): r""" - Determine if ``self`` prepended with ``s`` is still a reduced word of an - FC element in the Coxeter system. + Determine if ``self`` prepended with ``s`` is still a reduced word of + an FC element in the Coxeter system. INPUT: @@ -476,23 +629,103 @@ def still_reduced_fc_after_prepending(self, s): x, y= u.coset_decomposition({s, t}) return len(x) != m[s,t]-1 + + ########################################################################### + # Application of coset decompositions, II: Star operations # + ########################################################################### + + # Generalized star operations + def star_operation(self, J, direction, side = 'left'): + r""" + Apply a star operation on ``self`` relative to two noncommuting generators. + + Star operations were first defined on elements of Coxeter groups by + Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators `s,t` + such that `m(s,t)=3`. Later, Lusztig generalized the definition in + [Lus1985]_, via coset decompositions, to allow star operations with + respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given + such a pair, we can potentially perform four types of star operations + corresponding to all combinations of a 'side' and a 'direction': + left upper, left lower, right upper and right lower; see [Gre2006]_. + + Let `w` be an element in `W` and let `J` be any pair `\{s, t\}` of + noncommuting generators in `S`. Consider the coset decomposition `w = w_J + \cdot {}^J w` of `w` relative to `J`. Then an upper left star operation is + defined on `w` if and only if `1 \le l(w_J) \le m(s,t)-2`; when this is + the case, the operation returns `x\cdot w_J\cdot w^J` where `x` is the + letter `J` different from the leftmost letter of `w_J`. A lower left star + operation is defined on `w` if and only if `2 \le l(w_J) \le m(s,t)-1`; + when this is the case, the operation removes the leftmost letter of `w_J` + from `w`. Similar facts hold for right upper star operations. See + [Gre2006]_. + + The facts of the previous paragraph hold in general, even if `w` is not + FC. Note that if `f` is a star operation of any kind, then for every + element `w \in W`, the elements `w` and `f(w)` are either both FC or both + not FC. + + + INPUT: + + - ``J`` -- a set of two integers representing two noncommuting + generators of the Coxeter system. - # Second application of coset decompositions: star operations. + - ``direction`` -- string, either 'upper' or 'lower'; the function + performs an upper or lower star operation as ``direction`` specifies. - # Star operations were first defined on elements of Coxeter groups by - # Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators `s,t` - # such that `m(s,t)=3`. Later, Lusztig generalized the definition in - # [Lus1985]_, via coset decompositions, to allow star operations with - # respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given - # such a pair, we can potentially perform four types of star operations: - # left upper, left lower, right upper and right lower; see [Gre2006]_. + - ``side`` -- string (default: 'left'); if 'right', perform a right star + operation. - # Template for all four types of star operations: + OUTPUT: + + The result of the star operation if it is defined on ``self``, ``None`` + otherwise. + + + EXAMPLES:: + + We will compute all star operations on the following FC element in type + `B_6` relative to `J = \{5, 6\}` :: + + sage: FC = FullyCommutativeElements(['B', 6]) + sage: w = FC([1, 6, 2, 5, 4, 6, 5]) + + Whether and how a left star operations can be applied depend on the coset + decomposition `w = w_J \cdot w^J` :: + + sage: w.coset_decomposition({5, 6}) + ([6, 5, 6], [1, 2, 4, 5]) + + Only the lower star operation is defined on the left for this example :: + + sage: w.star_operation({5,6}, 'upper') + + sage: w.star_operation({5,6}, 'lower') + [1, 5, 2, 4, 6, 5] + + Whether and how a right star operations can be applied depend on the coset + decomposition `w = w^J \cdot w_J` :: + + sage: w.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6, 5]) + + Both types of right star operations on defined for this example :: + + sage: w.star_operation({5, 6}, 'upper', side='right') + [1, 6, 2, 5, 4, 6, 5, 6] + + sage: w.star_operation({5, 6}, 'lower', side='right') + [1, 6, 2, 5, 4, 6] + + + """ - def _star_operation_inner(self, J, direction, side): assert len(J) == 2, 'J needs to contain a pair of generators.' + assert direction = 'upper' or direction == 'lower', 'The direction + argument needs to be "upper" or "lower".' s, t = J mst = self.parent().coxeter_matrix()[s,t] + # Perform the coset decomposition on the specified side: if side == 'left': (string, remaining) = self.coset_decomposition(J, side=side) @@ -502,133 +735,29 @@ def _star_operation_inner(self, J, direction, side): cur_string = list(string) # From the coset decomposition, perform the upper or lower operation: - if direction == 'down' and 2 <= len(string) <= mst - 1: - # The lower star operation is defined if the parabolic factor `w_J` - # from the coset decomposition has length at least 2; when defined, it - # removes the outmost letter in `w_J`: - # decrease the length of the J-string: + # the lower star operation new_string = cur_string[1:] if side == 'left' else cur_string[:-1] elif direction == 'up' and 1 <= len(string) <= mst - 2: - # The upper star operation is defined if the parabolic factor `w_J` - # from the coset decomposition has length at most `m(s,t)-2`; when - # defined, it adds the appropriate letter `x` from `J` to `w_J`, - # extending its length by 1. + # the upper star operation ending_letter = cur_string[0] if side == 'left' else cur_string[-1] other = next(x for x in J if x != ending_letter) new_string = [other] + cur_string if side == 'left' else cur_string + [other] else: return None - # concatenate in the appropriate order + # concatenate w_J and w^J in the appropriate order combined_data = new_string + list(remaining) if side == 'left' else list(remaining) + new_string + # return the result of the star operation in its canonical form return self.parent().element_class(self.parent(), combined_data, check=False) - def upper_star(self, J, side='left'): - r""" - Perform an upper star operation on ``self`` with respect to the - parabolic subgroup generated by ``J``. - - INPUT: - - - ``J`` -- a set of two integers representing two non-commuting - generators of the Coxeter system. - - OUTPUT: - - The result of the star operation if it is defined on ``self``, ``None`` - otherwise. - - OPTIONAL ARGUEMNTS: - - - ``side`` -- string (default: 'left'); if set to 'right', perform a - right upper star operation. - - - EXAMPLES:: - - sage: FC = FullyCommutativeElements(['B', 6]) - sage: w = FC([1, 6, 2, 5, 4, 6, 5]) - sage: w.coset_decomposition({5, 6}) - ([6, 5, 6], [1, 2, 4, 5]) - sage: w.upper_star({5,6}) - - sage: w.coset_decomposition({5, 6}, side='right') - ([1, 6, 2, 5, 4], [6, 5]) - sage: w.upper_star((5, 6), side='right') - [1, 6, 2, 5, 4, 6, 5, 6] - sage: v = FC([1, 6, 2, 5, 4, 6]) - sage: v.coset_decomposition({5, 6}, side='right') - ([1, 6, 2, 5, 4], [6]) - sage: v.upper_star({5, 6}, side='right') - [1, 6, 2, 5, 4, 6, 5] - - .. NOTE:: - - As the examples illustrate, the left upper star operation is - defined if and only if in the coset decomposition `w = w_J \cdot - {}^J w`, the parabolic part has length `1\le l(w_J) \le m(s,t)-2`. - When this is the case, the operation returns `x\cdot w_J\cdot w^J` - where `x` is the letter `J` different from the leftmost letter of - `w_J`. Similar facts hold for right upper star operations. See - [Gre2006]_. - """ - return self._star_operation_inner(J, 'up', side) - - def lower_star(self, J, side='left'): - r""" - Perform a lower star operation on ``self`` with respect to the parabolic - subgroup generated by ``J``. - - INPUT: - - - ``J`` -- a set of two integers representing two non-commuting - generators of the Coxeter system. - - OUTPUT: - - The result of the star operation if it is defined on ``self``, ``None`` - otherwise. - - OPTIONAL ARGUEMNTS: - - - ``side`` -- string (default: 'left'); if set to 'right', perform a - right lower star operation. - - - EXAMPLES:: - - sage: FC = FullyCommutativeElements(['B', 6]) - sage: w = FC([1, 6, 2, 5, 4, 6, 5]) - sage: w.coset_decomposition({5, 6}) - ([6, 5, 6], [1, 2, 4, 5]) - sage: w.lower_star({5,6}) - [1, 5, 2, 4, 6, 5] - sage: w.lower_star({5,6}).coset_decomposition({5, 6}) - ([5, 6], [1, 2, 4, 5]) - sage: w.coset_decomposition({5, 6}, side='right') - ([1, 6, 2, 5, 4], [6, 5]) - sage: w.lower_star((5, 6), side='right') - [1, 6, 2, 5, 4, 6] - sage: v = FC([1, 6, 2, 5, 4, 6]) - sage: v.coset_decomposition({5, 6}, side='right') - ([1, 6, 2, 5, 4], [6]) - sage: v.lower_star({5, 6}, side='right') - - .. NOTE:: - - As the examples illustrate, the left lower star operation is - defined if and only if in the coset decomposition `w = w_J \cdot - {}^J w`, the parabolic part has length `2\le l(w_J) \le m(s,t)-1`. - When this is the case, the operation removes the leftmost letter of - `w_J` from `w`. Similar facts hold for right upper star operations. - See [Gre2006]_. - """ - return self._star_operation_inner(J, 'down', side) + ########################################################################### + # More on star operations: Fully commutative Kazhdan--Lusztig cells # + ########################################################################### # Star operations are important to the study of Kazhdan--Lusztig cells in # Coxeter groups; see, for example, [KL1979]_ and [GX2020]_. The orbits of @@ -699,130 +828,6 @@ def star_orbit(self, side='left', **kargs): return closure - ########## Heaps ########## - # Heaps are certain posets that are very useful for representing and - # studying FC elements; see, for example, [Ste1996]_ and [GX2020]_. - - def heap(self, **kargs): - r""" - Create the heap poset of ``self``. - - The heap of an FC element `w` is a labeled poset that can be defined - from any reduced word of `w`. Different reduced words yield isomorphic - labeled posets, so the heap is well defined. - - INPUT: - - - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. - - OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` - and where each element `i` carries `s_i` as its label. - - OPTIONAL ARGUMENTS: - - - ``one_index`` -- boolean (default: False). Setting the value to True - will change the underlying set of the poset to `\{1, 2, \dots, n\}`. - - - ``display_labeling`` -- boolean (default: False). Setting the value to - True will display the label `s_i` for each element `i` of the poset. - - EXAMPLES:: - - sage: FC = FullyCommutativeElements(['A', 5]) - sage: FC([1, 4, 3, 5, 2, 4]).heap().cover_relations() - [[1, 2], [1, 3], [2, 5], [2, 4], [3, 5], [0, 4]] - sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() - [[2, 3], [2, 4], [3, 6], [3, 5], [4, 6], [1, 5]] - - .. NOTE:: - - The partial order in the heap is defined by declaring `i\prec - j` if `i= 3} - for other in neighbors: - highest_level = max((j + 1 for j in range(level_zero_index) if other in letters_at_level[j]), default=None) - if highest_level: - graphics.append(plot.line([(other, highest_level), (x, level)], color='black', zorder=0)) - - g = sum(graphics) - g.axes(False) - return g class FullyCommutativeElements(Parent): r""" @@ -876,7 +881,7 @@ class FullyCommutativeElements(Parent): sage: len(FCB8) # long time (7 seconds) 14299 - Iterate through the FC elements of length up to 3 in the non FC-finite + Iterate through the FC elements of length up to 3 in the non-FC-finite group affine `A_2` :: sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) From 9e0e553f6451457cd96722e5f474e341ddc9e3e6 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Mon, 13 Jul 2020 14:24:08 -0600 Subject: [PATCH 117/379] debug for new star operation --- src/sage/combinat/fully_commutative_elements.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 3f6eba15aee..cdbace495fe 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -533,6 +533,7 @@ def coset_decomposition(self, J, side='left'): # The following function uses coset decompositions and will help us # generate all FC elements in a Coxeter group by induction on length. + def still_reduced_fc_after_prepending(self, s): r""" Determine if ``self`` prepended with ``s`` is still a reduced word of @@ -612,7 +613,6 @@ def still_reduced_fc_after_prepending(self, s): See Lemma 4.1 of [Ste1996]_. """ - m = self.parent().coxeter_matrix() if self.has_descent(s): return False @@ -645,8 +645,8 @@ def star_operation(self, J, direction, side = 'left'): [Lus1985]_, via coset decompositions, to allow star operations with respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given such a pair, we can potentially perform four types of star operations - corresponding to all combinations of a 'side' and a 'direction': - left upper, left lower, right upper and right lower; see [Gre2006]_. + corresponding to all combinations of a 'direction' and a 'side': + upper left, lower left, upper right and lower right; see [Gre2006]_. Let `w` be an element in `W` and let `J` be any pair `\{s, t\}` of noncommuting generators in `S`. Consider the coset decomposition `w = w_J @@ -716,13 +716,8 @@ def star_operation(self, J, direction, side = 'left'): sage: w.star_operation({5, 6}, 'lower', side='right') [1, 6, 2, 5, 4, 6] - - """ - assert len(J) == 2, 'J needs to contain a pair of generators.' - assert direction = 'upper' or direction == 'lower', 'The direction - argument needs to be "upper" or "lower".' s, t = J mst = self.parent().coxeter_matrix()[s,t] @@ -735,10 +730,10 @@ def star_operation(self, J, direction, side = 'left'): cur_string = list(string) # From the coset decomposition, perform the upper or lower operation: - if direction == 'down' and 2 <= len(string) <= mst - 1: + if direction == 'lower' and 2 <= len(string) <= mst - 1: # the lower star operation new_string = cur_string[1:] if side == 'left' else cur_string[:-1] - elif direction == 'up' and 1 <= len(string) <= mst - 2: + elif direction == 'upper' and 1 <= len(string) <= mst - 2: # the upper star operation ending_letter = cur_string[0] if side == 'left' else cur_string[-1] other = next(x for x in J if x != ending_letter) From a3c2fb59e80f33adf5c4eb92ac2e14f6b438ef37 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Mon, 13 Jul 2020 14:59:56 -0600 Subject: [PATCH 118/379] reorder functions, reorganize star operations, improve docstrings --- .../combinat/fully_commutative_elements.py | 177 +++++++++--------- 1 file changed, 87 insertions(+), 90 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index cdbace495fe..cd1e078b63c 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -64,7 +64,7 @@ def is_fully_commutative(self): :func:`check` is an alias of this method, and is called automatically when an element is created. - EXAMPLES: + EXAMPLES:: To construct an FC element, first call the parent class FullyCommutativeElements. The parent class contains information about @@ -111,9 +111,7 @@ def is_fully_commutative(self): ... ValueError: The input is not a reduced word of a fully commutative element. - """ - matrix = self.parent().coxeter_matrix() w = tuple(self) @@ -132,6 +130,7 @@ def commute_once(word, i): return word[:i] + (word[i+1], word[i]) + word[i+2:] not_fc = ValueError('The input is not a reduced word of a fully commutative element.') + if contains_long_braid(w): raise not_fc else: @@ -158,7 +157,7 @@ def cartier_foata_form(self): :func:`normalize` is an alias of this method, and is called automatically when an element is created. - EXAMPLES: + EXAMPLES:: The following reduced words express the same FC elements in `B_5` :: @@ -258,12 +257,13 @@ def plot_heap(self): r""" Display the Hasse diagram of the heap of ``self``. - The Hasse diagram is rendered in the lattice `S \times \mathbb{N}`, with - every element `i` in the poset drawn as a point labelled by its label - `s_i`. Every point is placed in the column for its label at a certain - level. The levels start at 0 and the level k of an element `i` is the - maximal number `k` such that the heap contains a chain `i_0\prec - i_1\prec ... \prec i_k` where `i_k=i`. See [Ste1996]_ and [GX2020]_. + The Hasse diagram is rendered in the lattice `S \times \mathbb{N}`, + with every element `i` in the poset drawn as a point labelled by its + label `s_i`. Every point is placed in the column for its label at a + certain level. The levels start at 0 and the level k of an element `i` + is the maximal number `k` such that the heap contains a chain + `i_0\prec i_1\prec ... \prec i_k` where `i_k=i`. See [Ste1996]_ and + [GX2020]_. OUTPUT: GraphicsObject @@ -319,7 +319,6 @@ def n_value(self): 2 sage: FC([1,3,2,5]).n_value() 3 - """ return self.heap().width() @@ -380,7 +379,6 @@ def find_descent(self, s, side='left'): sage: w.find_descent(3) - """ m = self.parent().coxeter_matrix() view = list(self) if side == 'left' else self[::-1] @@ -435,7 +433,6 @@ def descents(self, side='left'): EXAMPLES:: - sage: FC = FullyCommutativeElements(['B', 5]) sage: w = FC([1, 4, 3, 5, 2, 4, 3]) sage: sorted(w.descents()) @@ -501,7 +498,6 @@ def coset_decomposition(self, J, side='left'): algorithm works for all elements in Coxeter group, but it becomes especially simple for FC elements because descents are easier to find for FC elements. - """ string = [] # The J-string remaining = self.clone() # The remainder @@ -636,87 +632,88 @@ def still_reduced_fc_after_prepending(self, s): # Generalized star operations def star_operation(self, J, direction, side = 'left'): - r""" - Apply a star operation on ``self`` relative to two noncommuting generators. - - Star operations were first defined on elements of Coxeter groups by - Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators `s,t` - such that `m(s,t)=3`. Later, Lusztig generalized the definition in - [Lus1985]_, via coset decompositions, to allow star operations with - respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given - such a pair, we can potentially perform four types of star operations - corresponding to all combinations of a 'direction' and a 'side': - upper left, lower left, upper right and lower right; see [Gre2006]_. - - Let `w` be an element in `W` and let `J` be any pair `\{s, t\}` of - noncommuting generators in `S`. Consider the coset decomposition `w = w_J - \cdot {}^J w` of `w` relative to `J`. Then an upper left star operation is - defined on `w` if and only if `1 \le l(w_J) \le m(s,t)-2`; when this is - the case, the operation returns `x\cdot w_J\cdot w^J` where `x` is the - letter `J` different from the leftmost letter of `w_J`. A lower left star - operation is defined on `w` if and only if `2 \le l(w_J) \le m(s,t)-1`; - when this is the case, the operation removes the leftmost letter of `w_J` - from `w`. Similar facts hold for right upper star operations. See - [Gre2006]_. - - The facts of the previous paragraph hold in general, even if `w` is not - FC. Note that if `f` is a star operation of any kind, then for every - element `w \in W`, the elements `w` and `f(w)` are either both FC or both - not FC. + r""" + Apply a star operation on ``self`` relative to two noncommuting + generators. + + Star operations were first defined on elements of Coxeter groups by + Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators + `s,t` such that `m(s,t)=3`. Later, Lusztig generalized the definition + in [Lus1985]_, via coset decompositions, to allow star operations with + respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given + such a pair, we can potentially perform four types of star operations + corresponding to all combinations of a 'direction' and a 'side': upper + left, lower left, upper right and lower right; see [Gre2006]_. + + Let `w` be an element in `W` and let `J` be any pair `\{s, t\}` of + noncommuting generators in `S`. Consider the coset decomposition `w = + w_J \cdot {}^J w` of `w` relative to `J`. Then an upper left star + operation is defined on `w` if and only if `1 \le l(w_J) \le + m(s,t)-2`; when this is the case, the operation returns `x\cdot + w_J\cdot w^J` where `x` is the letter `J` different from the leftmost + letter of `w_J`. A lower left star operation is defined on `w` if and + only if `2 \le l(w_J) \le m(s,t)-1`; when this is the case, the + operation removes the leftmost letter of `w_J` from `w`. Similar + facts hold for right upper star operations. See [Gre2006]_. + + The facts of the previous paragraph hold in general, even if `w` is + not FC. Note that if `f` is a star operation of any kind, then for + every element `w \in W`, the elements `w` and `f(w)` are either both + FC or both not FC. - INPUT: + INPUT: - - ``J`` -- a set of two integers representing two noncommuting - generators of the Coxeter system. + - ``J`` -- a set of two integers representing two noncommuting + generators of the Coxeter system. - - ``direction`` -- string, either 'upper' or 'lower'; the function - performs an upper or lower star operation as ``direction`` specifies. + - ``direction`` -- string, either 'upper' or 'lower'; the function + performs an upper or lower star operation as ``direction`` specifies. - - ``side`` -- string (default: 'left'); if 'right', perform a right star - operation. + - ``side`` -- string (default: 'left'); if 'right', perform a right star + operation. - OUTPUT: + OUTPUT: - The result of the star operation if it is defined on ``self``, ``None`` - otherwise. + The result of the star operation if it is defined on ``self``, ``None`` + otherwise. - EXAMPLES:: - - We will compute all star operations on the following FC element in type - `B_6` relative to `J = \{5, 6\}` :: + EXAMPLES:: + + We will compute all star operations on the following FC element in type + `B_6` relative to `J = \{5, 6\}` :: - sage: FC = FullyCommutativeElements(['B', 6]) - sage: w = FC([1, 6, 2, 5, 4, 6, 5]) + sage: FC = FullyCommutativeElements(['B', 6]) + sage: w = FC([1, 6, 2, 5, 4, 6, 5]) - Whether and how a left star operations can be applied depend on the coset - decomposition `w = w_J \cdot w^J` :: + Whether and how a left star operations can be applied depend on the + coset decomposition `w = w_J \cdot w^J` :: - sage: w.coset_decomposition({5, 6}) - ([6, 5, 6], [1, 2, 4, 5]) + sage: w.coset_decomposition({5, 6}) + ([6, 5, 6], [1, 2, 4, 5]) - Only the lower star operation is defined on the left for this example :: + Only the lower star operation is defined on the left for this example :: - sage: w.star_operation({5,6}, 'upper') - - sage: w.star_operation({5,6}, 'lower') - [1, 5, 2, 4, 6, 5] + sage: w.star_operation({5,6}, 'upper') + + sage: w.star_operation({5,6}, 'lower') + [1, 5, 2, 4, 6, 5] - Whether and how a right star operations can be applied depend on the coset - decomposition `w = w^J \cdot w_J` :: + Whether and how a right star operations can be applied depend on the + coset decomposition `w = w^J \cdot w_J` :: - sage: w.coset_decomposition({5, 6}, side='right') - ([1, 6, 2, 5, 4], [6, 5]) + sage: w.coset_decomposition({5, 6}, side='right') + ([1, 6, 2, 5, 4], [6, 5]) - Both types of right star operations on defined for this example :: + Both types of right star operations on defined for this example :: - sage: w.star_operation({5, 6}, 'upper', side='right') - [1, 6, 2, 5, 4, 6, 5, 6] + sage: w.star_operation({5, 6}, 'upper', side='right') + [1, 6, 2, 5, 4, 6, 5, 6] - sage: w.star_operation({5, 6}, 'lower', side='right') - [1, 6, 2, 5, 4, 6] - """ + sage: w.star_operation({5, 6}, 'lower', side='right') + [1, 6, 2, 5, 4, 6] + """ assert len(J) == 2, 'J needs to contain a pair of generators.' s, t = J mst = self.parent().coxeter_matrix()[s,t] @@ -760,17 +757,17 @@ def star_operation(self, J, direction, side = 'left'): # Kazhdan--Lusztig cells. For example, ... - def star_orbit(self, side='left', **kargs): + def star_closure(self, side='left', **kargs): r""" - Compute the star operation orbit of ``self``. + Compute the star operation closure of ``self``. OUTPUT: The set containing all elements that can be obtained from ``self`` via a sequence of star operation of the specified type. OPTIONAL ARGUMENTS: - - ``side`` -- string (default: 'left'); if set to 'right', the function - compute the orbit for the specified type of right star operations. + - ``side`` -- string (default: 'left'); if this is set to 'right', the + function compute the closure for right star operations. - ``upper_only`` -- boolean (default: False); if passed, compute only the set of elements that can be obtained from ``self`` via upper star operations on the specified side. @@ -779,14 +776,14 @@ def star_orbit(self, side='left', **kargs): operations on the specified side. - EXAMPLES: + EXAMPLES:: Compute the left star closure of [1] in the group `I_8`. This should be set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same as the left *upper* star closure :: sage: FC = FullyCommutativeElements(['I', 8]) - sage: sorted(FC([1]).star_orbit()) + sage: sorted(FC([1]).star_closure()) [[1], [1, 2, 1], [1, 2, 1, 2, 1], @@ -794,17 +791,17 @@ def star_orbit(self, side='left', **kargs): [2, 1], [2, 1, 2, 1], [2, 1, 2, 1, 2, 1]] - sage: FC([1]).star_orbit() == FC([1]).star_orbit(upper_only=True) + sage: FC([1]).star_closure() == FC([1]).star_closure(upper_only=True) True """ m = self.parent().coxeter_matrix() adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] - directions = {'up', 'down'} + directions = {'upper', 'lower'} if 'upper_only' in kargs and kargs['upper_only']: - directions = {'up'} + directions = {'upper'} elif 'lower_only' in kargs and kargs['lower_only']: - directions = {'down'} + directions = {'lower'} closure = {self} recent_words = {self} @@ -813,7 +810,7 @@ def star_orbit(self, side='left', **kargs): for w in recent_words: for J in adjacent_pairs: for d in directions: - n = w._star_operation_inner(J, d, side) + n = w.star_operation(J, d, side) if n is not None and n not in closure: new_words.add(n) if len(new_words) == 0: @@ -826,7 +823,7 @@ def star_orbit(self, side='left', **kargs): class FullyCommutativeElements(Parent): r""" - Class for the set of fully commutative (FC) elements of a Coxeter systems + Class for the set of fully commutative (FC) elements of a Coxeter system. Coxeter systems with finitely many FC elements, or *FC-finite* Coxeter systems, are classfied by Stembridge in [Ste1996]_. They fall into seven @@ -847,7 +844,7 @@ class FullyCommutativeElements(Parent): enumerated sets or finite enumerated sets depending on if the group is FC-finite. - EXAMPLES: + EXAMPLES:: Enumerate the FC elements in `A_3` in their Cartier--Foata forms :: @@ -890,7 +887,7 @@ class FullyCommutativeElements(Parent): sage: FCA3([1,2,1]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative element. + ValueError: The input is not a reduced word of a fully commutative element. Elements are normalized to Cartier--Foata normal form upon construction :: From dd702ac066149424563a311415b7ac06a8c3b1d7 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 14 Jul 2020 04:17:31 +0000 Subject: [PATCH 119/379] Fix docstring syntax errors --- src/sage/combinat/fully_commutative_elements.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index cd1e078b63c..2019dc4a78b 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -64,7 +64,7 @@ def is_fully_commutative(self): :func:`check` is an alias of this method, and is called automatically when an element is created. - EXAMPLES:: + EXAMPLES: To construct an FC element, first call the parent class FullyCommutativeElements. The parent class contains information about @@ -157,7 +157,7 @@ def cartier_foata_form(self): :func:`normalize` is an alias of this method, and is called automatically when an element is created. - EXAMPLES:: + EXAMPLES: The following reduced words express the same FC elements in `B_5` :: @@ -679,7 +679,7 @@ def star_operation(self, J, direction, side = 'left'): otherwise. - EXAMPLES:: + EXAMPLES: We will compute all star operations on the following FC element in type `B_6` relative to `J = \{5, 6\}` :: @@ -776,7 +776,7 @@ def star_closure(self, side='left', **kargs): operations on the specified side. - EXAMPLES:: + EXAMPLES: Compute the left star closure of [1] in the group `I_8`. This should be set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same @@ -844,7 +844,7 @@ class FullyCommutativeElements(Parent): enumerated sets or finite enumerated sets depending on if the group is FC-finite. - EXAMPLES:: + EXAMPLES: Enumerate the FC elements in `A_3` in their Cartier--Foata forms :: From bfcdbf9958e9a8df4672fab270975d04f2ad7bb6 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Fri, 24 Jul 2020 16:30:24 -0600 Subject: [PATCH 120/379] more docstring edits --- .../combinat/fully_commutative_elements.py | 425 +++++++++--------- 1 file changed, 213 insertions(+), 212 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 2019dc4a78b..083471a82f3 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -2,12 +2,11 @@ Fully commutative elements of Coxeter groups An element `w` in a Coxeter system (W,S) is fully commutative (FC) if -every two reduced word of w can be related by a sequence of only +every two reduced words of w can be related by a sequence of only commutation relations, i.e., relations of the form `st=ts` where `s,t` are commuting generators in `S`. See [Ste1996]_. """ from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import NormalizedClonableList from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -17,20 +16,20 @@ from sage.combinat.posets.posets import Poset import itertools + class FullyCommutativeElement(NormalizedClonableList): r""" - A (reduced word of a) fully commutative (FC) element in a Coxeter system. + A fully commutative (FC) element in a Coxeter system. - An element `w` in a Coxeter system (W,S) is fully commutative (FC) if - every two reduced word of w can be related by a sequence of only - commutation relations, i.e., relations of the form `st=ts` where `s,t` are - commuting generators in `S`. + An element `w` in a Coxeter system (W,S) is fully commutative (FC) if every + two reduced word of w can be related by a sequence of only commutation + relations, i.e., relations of the form `st=ts` where `s,t` are commuting + generators in `S`. Every FC element has a canonical reduced word called its Cartier--Foata - form. See [Gre2006]_. We will normalize each FC element to this form. - + form. See [Gre2006]_. We will normalize each FC element to this form. """ - + # Methods required as a subclass of NormalizedClonableList: def check(self): r""" @@ -59,7 +58,7 @@ def is_fully_commutative(self): characterization that an element `w` in a Coxeter system `(W,S)` is FC if and only if for every pair of generators `s,t \in S` for which `m(s,t)>2`, no reduced word of `w` contains the 'braid' word `sts...` - of length `m(s,t)` as a contiguous subword. See [Ste1996]_. + of length `m(s,t)` as a contiguous subword. See [Ste1996]_. :func:`check` is an alias of this method, and is called automatically when an element is created. @@ -68,7 +67,7 @@ def is_fully_commutative(self): To construct an FC element, first call the parent class FullyCommutativeElements. The parent class contains information about - the Coxeter matrix of the ambient Coxeter system :: + the Coxeter matrix of the ambient Coxeter system :: sage: FC = FullyCommutativeElements(['B', 3]) sage: FC.coxeter_matrix() @@ -81,7 +80,7 @@ def is_fully_commutative(self): sage: FC([]) [] sage: FC([1,2]) - [1, 2] + [1, 2] sage: FC([2,3,2]) [2, 3, 2] sage: FC([3,2,3]) @@ -93,7 +92,7 @@ def is_fully_commutative(self): sage: FC([3,1]) [1, 3] sage: FC([2,3,1]) - [2, 1, 3] + [2, 1, 3] sage: FC([1,3]) == FC([3,1]) True @@ -104,7 +103,7 @@ def is_fully_commutative(self): Traceback (most recent call last): ... ValueError: The input is not a reduced word of a fully commutative - element. + element. sage: FC([2,3,2,3]) Traceback (most recent call last): @@ -116,29 +115,30 @@ def is_fully_commutative(self): w = tuple(self) def contains_long_braid(w): - for i in range(0, len(w)-2): + for i in range(0, len(w) - 2): a = w[i] - b = w[i+1] + b = w[i + 1] m = matrix[a, b] - if m > 2 and i+m <= len(w): + if m > 2 and i + m <= len(w): ab_braid = (a, b) * (m // 2) + ((a,) if m % 2 == 1 else ()) - if w[i:i+m] == ab_braid: + if w[i:i + m] == ab_braid: return True return False def commute_once(word, i): - return word[:i] + (word[i+1], word[i]) + word[i+2:] + return word[:i] + (word[i + 1], word[i]) + word[i + 2:] - not_fc = ValueError('The input is not a reduced word of a fully commutative element.') + not_fc = ValueError( + 'The input is not a reduced word of a fully commutative element.') if contains_long_braid(w): raise not_fc else: - l, checked, queue = len(w), {w}, deque([w]) + l, checked, queue = len(w), {w}, deque([w]) while queue: word = queue.pop() - for i in range(l-1): - a, b = word[i], word[i+1] + for i in range(l - 1): + a, b = word[i], word[i + 1] if matrix[a, b] == 2: new_word = commute_once(word, i) if new_word not in checked: @@ -149,7 +149,7 @@ def commute_once(word, i): queue.appendleft(new_word) return True - # Representing FC elements: Canonical forms + # Representing FC elements: Canonical forms def cartier_foata_form(self): r""" Return the Cartier--Foata normal form of ``self``. @@ -171,20 +171,20 @@ def cartier_foata_form(self): .. NOTE:: - The Cartier--Foata form of a reduced word of an FC element w - can be found recursively by repeatedly moving left descents of + The Cartier--Foata form of a reduced word of an FC element `w` can + be found recursively by repeatedly moving left descents of elements to the left and ordering the left descents from small to - large. In the above example, the left descents of the element are 4 - and 1, therefore the Cartier--Foata form of the element is the - concatenation of [1,4] with the Cartier--Foata form of the remaining - part of the word. See [Gre2006]_. + large. In the above example, the left descents of the element are + 4 and 1, therefore the Cartier--Foata form of the element is the + concatenation of [1,4] with the Cartier--Foata form of the + remaining part of the word. See [Gre2006]_. .. SEEALSO:: :func:`descents` """ self._require_mutable() out_word = [] - + while len(self) > 0: fronts = self.descents() out_word.extend(sorted(fronts)) @@ -193,16 +193,14 @@ def cartier_foata_form(self): self._set_list(out_word) - - # Representing FC elements: Heaps - + # Representing FC elements: Heaps def heap(self, **kargs): r""" Create the heap poset of ``self``. The heap of an FC element `w` is a labeled poset that can be defined from any reduced word of `w`. Different reduced words yield isomorphic - labeled posets, so the heap is well defined. + labeled posets, so the heap is well defined. Heaps are very useful for visualizing and studying FC elements; see, for example, [Ste1996]_ and [GX2020]_. @@ -212,15 +210,18 @@ def heap(self, **kargs): - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` - and where each element `i` carries `s_i` as its label. + and where each element `i` carries `s_i` as its label. The partial + order `\prec` on the poset is defined by declaring `i\prec j` if `i= 3} for other in neighbors: - highest_level = max((j + 1 for j in range(level_zero_index) if other in letters_at_level[j]), default=None) + highest_level = max( + (j + 1 for j in range(level_zero_index) if other in letters_at_level[j]), default=None) if highest_level: - graphics.append(plot.line([(other, highest_level), (x, level)], color='black', zorder=0)) - + graphics.append( + plot.line([(other, highest_level), (x, level)], color='black', zorder=0)) + g = sum(graphics) g.axes(False) return g @@ -306,7 +317,7 @@ def n_value(self): of any longest antichain) of its heap. The n-value is important as it coincides with Lusztig's a-value for FC elements in all Weyl and affine Weyl groups as well as so-called star-reducible groups; see - [GX2020]_. + [GX2020]_. EXAMPLES:: @@ -321,7 +332,6 @@ def n_value(self): 3 """ return self.heap().width() - ########################################################################### # Descents and coset decompositions of FC elements # @@ -340,23 +350,22 @@ def find_descent(self, s, side='left'): r""" Check if ``s`` is a descent of ``self`` and find its position if so. - Recall that a generator `s` is called a left or right descent of an - element `w` if `l(sw)` or `l(ws)` is smaller than `l(w)`, - respectively. If `w` is FC, then `s` is a left descent of `w` if and - only if `s` appears to in the word and every generator to the left of - the leftmost `s` in the word commutes with `s`. A similar - characterization exists for right descents of FC elements. - + A generator `s` is called a left or right descent of an element `w` if + `l(sw)` or `l(ws)` is smaller than `l(w)`, respectively. If `w` is FC, + then `s` is a left descent of `w` if and only if `s` appears to in the + word and every generator to the left of the leftmost `s` in the word + commutes with `s`. A similar characterization exists for right + descents of FC elements. INPUT: - ``s`` -- integer representing a generator of the Coxeter system. - OUTPUT: + OUTPUT: Determine if the generator ``s`` is a left descent of ``self``; return the index of the leftmost occurrence of ``s`` in ``self`` if so and - return ``None`` if not. + return ``None`` if not. OPTIONAL ARGUMENTS: @@ -419,12 +428,12 @@ def descents(self, side='left'): r""" Obtain the set of descents on the appropriate side of ``self``. - Recall that a generator `s` is called a left or right descent of an - element `w` if `l(sw)` or `l(ws)` is smaller than `l(w)`, - respectively. If `w` is FC, then `s` is a left descent of `w` if and - only if `s` appears to in the word and every generator to the left of - the leftmost `s` in the word commutes with `s`. A similar - characterization exists for right descents of FC elements. + A generator `s` is called a left or right descent of an element `w` if + `l(sw)` or `l(ws)` is smaller than `l(w)`, respectively. If `w` is FC, + then `s` is a left descent of `w` if and only if `s` appears to in the + word and every generator to the left of the leftmost `s` in the word + commutes with `s`. A similar characterization exists for right + descents of FC elements. OPTIONAL ARGUMENTS: @@ -456,10 +465,10 @@ def descents(self, side='left'): # Coset decompositions def coset_decomposition(self, J, side='left'): r""" - Return the coset decomposition of ``self`` with repsect to the parabolic - subgroup generated by ``J``. + Return the coset decomposition of ``self`` with repsect to the + parabolic subgroup generated by ``J``. - INPUT: + INPUT: - ``J`` -- subset of the generating set `S` of the Coxeter system. @@ -469,13 +478,13 @@ def coset_decomposition(self, J, side='left'): is generated by the elements in `J`, and `w^J` has no left descent from `J`. This tuple is unique and satisfies the equation `l(w) = l(w_J) + l(w^J)`, where `l` denotes Coxeter length, by general theory; - see Proposition 2.4.4 of [BB2005]_. + see Proposition 2.4.4 of [BB2005]_. OPTIONAL ARGUMENTS: - ``side`` -- string (default: 'left'); if the value is set to 'right', then the function returns the tuple `(w'^J, w'_J)` from the coset - decomposition `w = w'^J \cdot w'_J` of `w` with respect to `J`. + decomposition `w = w'^J \cdot w'_J` of `w` with respect to `J`. EXAMPLES:: @@ -497,10 +506,10 @@ def coset_decomposition(self, J, side='left'): are in `J` to the left"; see the proof of [BB2005]_. This greedy algorithm works for all elements in Coxeter group, but it becomes especially simple for FC elements because descents are easier to - find for FC elements. + find for FC elements. """ - string = [] # The J-string - remaining = self.clone() # The remainder + string = [] # to record w_J + remaining = self.clone() # to record w^J if side == 'right': remaining._set_list(remaining[::-1]) @@ -512,7 +521,7 @@ def coset_decomposition(self, J, side='left'): remaining.remove(x) else: break - + if side == 'right': remaining._set_list(remaining[::-1]) string = string[::-1] @@ -522,14 +531,12 @@ def coset_decomposition(self, J, side='left'): return (string, remaining) if side == 'left' else (remaining, string) - ########################################################################### # Application of coset decompositions, I: New FC elements from old # ########################################################################### # The following function uses coset decompositions and will help us - # generate all FC elements in a Coxeter group by induction on length. - + # generate all FC elements in a Coxeter group by induction on length. def still_reduced_fc_after_prepending(self, s): r""" Determine if ``self`` prepended with ``s`` is still a reduced word of @@ -541,7 +548,9 @@ def still_reduced_fc_after_prepending(self, s): - ``self`` -- a reduced word of an FC element - EXAMPLES:: + EXAMPLES: + + Consider the FC element `w = 12` in the group `B_3` :: sage: FCB3 = FullyCommutativeElements(['B', 3]) sage: FCB3.coxeter_matrix() @@ -549,13 +558,26 @@ def still_reduced_fc_after_prepending(self, s): [3 1 4] [2 4 1] sage: w = FCB3([1,2]) + + When `s=1`, `sw` is 112, which is not reduced :: + sage: w.still_reduced_fc_after_prepending(1) False + + + When `s=2`, `sw` is 212, which is reduced but not FC :: + sage: w.still_reduced_fc_after_prepending(2) False + + When `s=31, `sw` is 312, which is reduced and FC :: + sage: w.still_reduced_fc_after_prepending(3) True - sage: u = FCB3([3,1,2]) + + More examples :: + + sage: u = FCB3([3,1,2]) sage: u.still_reduced_fc_after_prepending(1) False sage: u.still_reduced_fc_after_prepending(2) @@ -568,22 +590,20 @@ def still_reduced_fc_after_prepending(self, s): sage: w.still_reduced_fc_after_prepending(5) False - - .. NOTE:: + .. NOTE:: If `w` is a reduced word of an element, then the concatenation `sw` is still a reduced word if and only if `s` is not a left - descent of `w` by general Coxeter group theory. So now assume `w` is - a reduced word of an FC element and `s` is not a left descent `w`. - In this case, Lemma 4.1 of [Ste1996]_ implies that `sw` is not a - reduced word of an FC element if and only if some letter in `w` does - not commute with `s` and the following conditions hold + descent of `w` by general Coxeter group theory. So now assume `w` + is a reduced word of an FC element and `s` is not a left descent + `w`. In this case, Lemma 4.1 of [Ste1996]_ implies that `sw` is + not a reduced word of an FC element if and only if some letter in + `w` does not commute with `s` and the following conditions hold simultaneously for the leftmost such letter `t`: - (1) `t` is left descent of the word `u_1` obtained by removing all letters to the left of the aforementioned `t` from `w`; - (this condition is automatically true) + (this condition is automatically true by definition of `u_1`) (2) `s` is left descent of the word `u_2` obtained by removing the leftmost `t` from `u_1`; @@ -604,7 +624,6 @@ def still_reduced_fc_after_prepending(self, s): decomposition `u_1 = u_J \cdot u^J` of `u_1` with respect to `J := \{s, t\}` is the element `tst...` of length `m(s,t)-1`. - REFERENCES: See Lemma 4.1 of [Ste1996]_. @@ -619,23 +638,21 @@ def still_reduced_fc_after_prepending(self, s): except StopIteration: return True - u = self.clone() u._set_list(self[j:]) - x, y= u.coset_decomposition({s, t}) - return len(x) != m[s,t]-1 + x, y = u.coset_decomposition({s, t}) + return len(x) != m[s, t] - 1 - ########################################################################### # Application of coset decompositions, II: Star operations # ########################################################################### - + # Generalized star operations - def star_operation(self, J, direction, side = 'left'): + def star_operation(self, J, direction, side='left'): r""" Apply a star operation on ``self`` relative to two noncommuting - generators. - + generators. + Star operations were first defined on elements of Coxeter groups by Kazhdan and Lusztig in [KL1979]_ with respect to pair of generators `s,t` such that `m(s,t)=3`. Later, Lusztig generalized the definition @@ -643,7 +660,7 @@ def star_operation(self, J, direction, side = 'left'): respect to any pair of generators `s,t` such that `m(s,t)\ge 3`. Given such a pair, we can potentially perform four types of star operations corresponding to all combinations of a 'direction' and a 'side': upper - left, lower left, upper right and lower right; see [Gre2006]_. + left, lower left, upper right and lower right; see [Gre2006]_. Let `w` be an element in `W` and let `J` be any pair `\{s, t\}` of noncommuting generators in `S`. Consider the coset decomposition `w = @@ -654,33 +671,31 @@ def star_operation(self, J, direction, side = 'left'): letter of `w_J`. A lower left star operation is defined on `w` if and only if `2 \le l(w_J) \le m(s,t)-1`; when this is the case, the operation removes the leftmost letter of `w_J` from `w`. Similar - facts hold for right upper star operations. See [Gre2006]_. + facts hold for right star operations. See [Gre2006]_. The facts of the previous paragraph hold in general, even if `w` is not FC. Note that if `f` is a star operation of any kind, then for every element `w \in W`, the elements `w` and `f(w)` are either both - FC or both not FC. - + FC or both not FC. INPUT: - ``J`` -- a set of two integers representing two noncommuting - generators of the Coxeter system. - - - ``direction`` -- string, either 'upper' or 'lower'; the function - performs an upper or lower star operation as ``direction`` specifies. + generators of the Coxeter system. - - ``side`` -- string (default: 'left'); if 'right', perform a right star - operation. + - ``direction`` -- string, 'upper' or 'lower'; the function performs + an upper or lower star operation according to ``direction``. - OUTPUT: + - ``side`` -- string (default: 'left'); if this is set to 'right', the + function performs a right star operation. - The result of the star operation if it is defined on ``self``, ``None`` - otherwise. + OUTPUT: + The Cartier--Foata form of the result of the star operation if the + operation is defined on ``self``, ``None`` otherwise. EXAMPLES: - + We will compute all star operations on the following FC element in type `B_6` relative to `J = \{5, 6\}` :: @@ -714,9 +729,9 @@ def star_operation(self, J, direction, side = 'left'): sage: w.star_operation({5, 6}, 'lower', side='right') [1, 6, 2, 5, 4, 6] """ - assert len(J) == 2, 'J needs to contain a pair of generators.' + assert len(J) == 2, 'J needs to contain a pair of generators.' s, t = J - mst = self.parent().coxeter_matrix()[s,t] + mst = self.parent().coxeter_matrix()[s, t] # Perform the coset decomposition on the specified side: if side == 'left': @@ -726,101 +741,27 @@ def star_operation(self, J, direction, side = 'left'): cur_string = list(string) - # From the coset decomposition, perform the upper or lower operation: + # From the coset decomposition, perform the upper or lower operation: if direction == 'lower' and 2 <= len(string) <= mst - 1: - # the lower star operation + # the lower star operation new_string = cur_string[1:] if side == 'left' else cur_string[:-1] elif direction == 'upper' and 1 <= len(string) <= mst - 2: - # the upper star operation + # the upper star operation ending_letter = cur_string[0] if side == 'left' else cur_string[-1] other = next(x for x in J if x != ending_letter) - new_string = [other] + cur_string if side == 'left' else cur_string + [other] + new_string = [other] + \ + cur_string if side == 'left' else cur_string + [other] else: return None # concatenate w_J and w^J in the appropriate order - combined_data = new_string + list(remaining) if side == 'left' else list(remaining) + new_string - + combined_data = new_string + \ + list(remaining) if side == 'left' else list(remaining) + new_string + # return the result of the star operation in its canonical form return self.parent().element_class(self.parent(), combined_data, check=False) - - - ########################################################################### - # More on star operations: Fully commutative Kazhdan--Lusztig cells # - ########################################################################### - - # Star operations are important to the study of Kazhdan--Lusztig cells in - # Coxeter groups; see, for example, [KL1979]_ and [GX2020]_. The orbits of - # suitable FC elements under star operations are often certain - # Kazhdan--Lusztig cells. For example, ... - - - def star_closure(self, side='left', **kargs): - r""" - Compute the star operation closure of ``self``. - - OUTPUT: The set containing all elements that can be obtained from - ``self`` via a sequence of star operation of the specified type. - - OPTIONAL ARGUMENTS: - - - ``side`` -- string (default: 'left'); if this is set to 'right', the - function compute the closure for right star operations. - - ``upper_only`` -- boolean (default: False); if passed, compute only - the set of elements that can be obtained from ``self`` via upper star - operations on the specified side. - - ``lower_only`` -- boolean (default: False); if passed, compute only - the set of elements that can be obtained from ``self`` via lower star - operations on the specified side. - - - EXAMPLES: - - Compute the left star closure of [1] in the group `I_8`. This should be - set of `\{1,2\}`-braids of lengths 1 through 7, and should be the same - as the left *upper* star closure :: - - sage: FC = FullyCommutativeElements(['I', 8]) - sage: sorted(FC([1]).star_closure()) - [[1], - [1, 2, 1], - [1, 2, 1, 2, 1], - [1, 2, 1, 2, 1, 2, 1], - [2, 1], - [2, 1, 2, 1], - [2, 1, 2, 1, 2, 1]] - sage: FC([1]).star_closure() == FC([1]).star_closure(upper_only=True) - True - """ - m = self.parent().coxeter_matrix() - adjacent_pairs = [(a, b) for (a, b) in itertools.product(self.parent().index_set(), repeat=2) if a < b and m[a,b] > 2] - - directions = {'upper', 'lower'} - if 'upper_only' in kargs and kargs['upper_only']: - directions = {'upper'} - elif 'lower_only' in kargs and kargs['lower_only']: - directions = {'lower'} - - closure = {self} - recent_words = {self} - while True: - new_words = set() - for w in recent_words: - for J in adjacent_pairs: - for d in directions: - n = w.star_operation(J, d, side) - if n is not None and n not in closure: - new_words.add(n) - if len(new_words) == 0: - break - closure.update(new_words) - recent_words = new_words - return closure - - - class FullyCommutativeElements(Parent): r""" Class for the set of fully commutative (FC) elements of a Coxeter system. @@ -828,7 +769,7 @@ class FullyCommutativeElements(Parent): Coxeter systems with finitely many FC elements, or *FC-finite* Coxeter systems, are classfied by Stembridge in [Ste1996]_. They fall into seven families, namely the groups of types `A_n, B_n, D_n, E_n, F_n, H_n` and - `I_2(m)`. + `I_2(m)`. INPUT: @@ -850,7 +791,7 @@ class FullyCommutativeElements(Parent): sage: FCA3 = FullyCommutativeElements(['A', 3]) sage: FCA3.category() - Category of finite enumerated sets + Category of enumerated sets sage: FCA3.list() [[], [1], @@ -873,7 +814,7 @@ class FullyCommutativeElements(Parent): sage: len(FCB8) # long time (7 seconds) 14299 - Iterate through the FC elements of length up to 3 in the non-FC-finite + Iterate through the FC elements of length up to 2 in the non-FC-finite group affine `A_2` :: sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) @@ -887,14 +828,14 @@ class FullyCommutativeElements(Parent): sage: FCA3([1,2,1]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative element. + ValueError: The input is not a reduced word of a fully commutative element. Elements are normalized to Cartier--Foata normal form upon construction :: sage: FCA3([2, 3, 1, 2]) [2, 1, 3, 2] - """ + def __init__(self, data): if isinstance(data, CoxeterMatrix): self._matrix = data @@ -902,7 +843,8 @@ def __init__(self, data): try: t = CartanType(data) except (TypeError, ValueError): - raise ValueError('Input must be a CoxeterMatrix or data describing a Cartan type.') + raise ValueError( + 'Input must be a CoxeterMatrix or data describing a Cartan type.') self._matrix = t.coxeter_matrix() self._index_set = sorted(self._matrix.index_set()) @@ -929,6 +871,21 @@ def coxeter_matrix(self): Obtain the Coxeter matrix of the associated Coxter system. OUTPUT: CoxeterMatrix + + EXAMPLES:: + + sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3.coxeter_matrix() + [1 3 2] + [3 1 3] + [2 3 1] + sage: FCB5 = FullyCommutativeElements(['B', 5]) + sage: FCB5.coxeter_matrix() + [1 3 2 2 2] + [3 1 3 2 2] + [2 3 1 3 2] + [2 2 3 1 4] + [2 2 2 4 1] """ return self._matrix @@ -938,6 +895,16 @@ def index_set(self): associated Coxeter system. OUTPUT: iterable of integers + + + EXAMPLES:: + + sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3.index_set() + [1, 2, 3] + sage: FCB5 = FullyCommutativeElements(['B', 5]) + sage: FCB5.index_set() + [1, 2, 3, 4, 5] """ return self._index_set @@ -965,10 +932,10 @@ def __iter__(self): for w in recent_words.keys(): for s in letters: if w.still_reduced_fc_after_prepending(s): - sw = self.element_class(self, [s] + list(w), check=False) + sw = self.element_class( + self, [s] + list(w), check=False) # "Add" sw to the "set" new_words[sw] = True - if len(new_words) == 0: return for w in new_words.keys(): @@ -983,10 +950,44 @@ def iterate_to_length(self, length): INPUT: - ``length`` -- integer; maximum length of element to generate. + + OUTPUT: generator for elements of ``self`` of length up to ``length`` + + EXAMPLES: + + The following example produces all FC elements of length up to 2 + in the group `A_3` :: + + sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: list(FCA3.iterate_to_length(2)) + [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3]] + + The lists for length 4 and 5 are the same since 4 is the maximum + length of an FC element in `A_3` :: + + sage: list(FCA3.iterate_to_length(4)) + [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], [3, 2, + 1], [2, 1, 3], [1, 3, 2], [1, 2, 3], [2, 1, 3, 2]] + sage: list(FCA3.iterate_to_length(5)) + [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], [3, 2, + 1], [2, 1, 3], [1, 3, 2], [1, 2, 3], [2, 1, 3, 2]] + sage: list(FCA3.iterate_to_length(4)) == list(FCA3) + True + + The following example produces all FC elements of length up to 4 + in the affine Weyl group `\tilde A_2` :: + + sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) + sage: FCAffineA2.category() + Category of infinite enumerated sets + sage: list(FCAffineA2.iterate_to_length(4)) + [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, + 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 1, + 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], [1, 0, 2, 1], [2, 1, + 0, 2], [2, 0, 1, 2]] """ assert length >= 0 for w in self: if len(w) > length: break yield w - From c5a8b9cffc8458f567a35c12f28350780401e6bc Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 24 Jul 2020 16:52:27 -0600 Subject: [PATCH 121/379] Clean up category situation; settle for detecting FC-finite only in known cases --- .../combinat/fully_commutative_elements.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 083471a82f3..da5b79a7c84 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -8,7 +8,7 @@ """ from sage.structure.parent import Parent from sage.structure.list_clone import NormalizedClonableList -from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from .root_system.coxeter_matrix import CoxeterMatrix from .root_system.cartan_type import CartanType @@ -791,7 +791,7 @@ class FullyCommutativeElements(Parent): sage: FCA3 = FullyCommutativeElements(['A', 3]) sage: FCA3.category() - Category of enumerated sets + Category of finite enumerated sets sage: FCA3.list() [[], [1], @@ -819,7 +819,7 @@ class FullyCommutativeElements(Parent): sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() - Category of infinite enumerated sets + Category of enumerated sets sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] @@ -849,15 +849,21 @@ def __init__(self, data): self._index_set = sorted(self._matrix.index_set()) - # Determine if this group is FC-finite. - category = InfiniteEnumeratedSets() + # Try to determine if this group is FC-finite, and refine our category + # to FiniteEnumeratedSets. This does not detect all FC-finite Coxeter + # matrices, but does detect ones with known Cartan types. + category = EnumeratedSets() if self._matrix.is_finite(): category = FiniteEnumeratedSets() else: - cartan_type = self._matrix.coxeter_type().cartan_type() - family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() - if not affine and (rank == 2 or family in {'A', 'B', 'C', 'D', 'E', 'F', 'H'}): - category = FiniteEnumeratedSets() + try: + cartan_type = self._matrix.coxeter_type().cartan_type() + family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() + if not affine and (rank == 2 or family in {'A', 'B', 'C', 'D', 'E', 'F', 'H'}): + category = FiniteEnumeratedSets() + except: + # Cannot recognize the matrix as a Cartan type + pass Parent.__init__(self, category=category) @@ -979,7 +985,7 @@ def iterate_to_length(self, length): sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() - Category of infinite enumerated sets + Category of enumerated sets sage: list(FCAffineA2.iterate_to_length(4)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 1, From cfab9ada7905d5f4a90062d0a326e7873cf0a343 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 24 Jul 2020 17:02:03 -0600 Subject: [PATCH 122/379] Minor docstring syntax errors; run line-length formatter --- .../combinat/fully_commutative_elements.py | 135 +++++++++--------- 1 file changed, 66 insertions(+), 69 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index da5b79a7c84..67844aa4744 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -57,8 +57,8 @@ def is_fully_commutative(self): To check if `self` is fully commutative, we use the well-known characterization that an element `w` in a Coxeter system `(W,S)` is FC if and only if for every pair of generators `s,t \in S` for which - `m(s,t)>2`, no reduced word of `w` contains the 'braid' word `sts...` - of length `m(s,t)` as a contiguous subword. See [Ste1996]_. + `m(s,t)>2`, no reduced word of `w` contains the 'braid' word `sts...` of + length `m(s,t)` as a contiguous subword. See [Ste1996]_. :func:`check` is an alias of this method, and is called automatically when an element is created. @@ -202,26 +202,25 @@ def heap(self, **kargs): from any reduced word of `w`. Different reduced words yield isomorphic labeled posets, so the heap is well defined. - Heaps are very useful for visualizing and studying FC elements; see, - for example, [Ste1996]_ and [GX2020]_. + Heaps are very useful for visualizing and studying FC elements; see, for + example, [Ste1996]_ and [GX2020]_. INPUT: - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` - and where each element `i` carries `s_i` as its label. The partial - order `\prec` on the poset is defined by declaring `i\prec j` if `i Date: Fri, 24 Jul 2020 18:25:22 -0600 Subject: [PATCH 123/379] Further refine cardinality detection --- .../combinat/fully_commutative_elements.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 67844aa4744..a54a5d3880d 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -9,7 +9,6 @@ from sage.structure.parent import Parent from sage.structure.list_clone import NormalizedClonableList from sage.categories.enumerated_sets import EnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from .root_system.coxeter_matrix import CoxeterMatrix from .root_system.cartan_type import CartanType from collections import deque @@ -816,7 +815,7 @@ class FullyCommutativeElements(Parent): sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() - Category of enumerated sets + Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] @@ -846,20 +845,27 @@ def __init__(self, data): self._index_set = sorted(self._matrix.index_set()) - # Try to determine if this group is FC-finite, and refine our category - # to FiniteEnumeratedSets. This does not detect all FC-finite Coxeter - # matrices, but does detect ones with known Cartan types. + + # Start with the category of enumerated sets and see if we can refine it + # to finite or infinite by detecting if the group is FC-finite. category = EnumeratedSets() if self._matrix.is_finite(): - category = FiniteEnumeratedSets() + # Finite groups will be FC-finite. + category = category.Finite() else: try: + # The only infinite Cartan types that are FC-finite are those + # affine types which correspond to F_5 and E_9 in Stembridge's + # notation, that is affine F4 and affine E8. cartan_type = self._matrix.coxeter_type().cartan_type() family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() - if not affine and (rank == 2 or family in {'A', 'B', 'C', 'D', 'E', 'F', 'H'}): - category = FiniteEnumeratedSets() + if affine: + category = category.Finite() if (family == 'F' and rank == 4) or (family == 'E' and rank == 8) else category.Infinite() + else: + category = category.Infinite() except: - # Cannot recognize the matrix as a Cartan type + # Cannot recognize the matrix as a Cartan type, just stay with + # plain enumerated sets. pass Parent.__init__(self, category=category) @@ -982,7 +988,7 @@ def iterate_to_length(self, length): sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() - Category of enumerated sets + Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(4)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 1, From 005fbcbb2502d051ca889a92dc6a8b785e42a4c9 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Tue, 28 Jul 2020 11:35:34 -0600 Subject: [PATCH 124/379] update references --- src/doc/en/reference/references/index.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ea0b248fbb2..a8e3bf97abe 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3798,6 +3798,9 @@ REFERENCES: and Related Topics, Advanced Studies in Pure mathematics 6, 1985, pp. 255-287. +.. [Lus2013] George Lusztig, *Hecke algebras with unequal parameters*, + :arxiv:`math/0208154`. + .. [Lut2005] Frank H. Lutz, "Triangulated Manifolds with Few Vertices: Combinatorial Manifolds", preprint (2005), :arxiv:`math/0506372` From e7483dbc50815eac3832581145fbfe5f66899028 Mon Sep 17 00:00:00 2001 From: Tianyuan Xu Date: Tue, 28 Jul 2020 13:27:00 -0600 Subject: [PATCH 125/379] edit docstring --- .../combinat/fully_commutative_elements.py | 171 +++++++++++------- 1 file changed, 101 insertions(+), 70 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index a54a5d3880d..d8488ffa926 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -5,7 +5,25 @@ every two reduced words of w can be related by a sequence of only commutation relations, i.e., relations of the form `st=ts` where `s,t` are commuting generators in `S`. See [Ste1996]_. + +Authors: + +- Chase Meadors, Tianyuan Xu (2020): Initial version + +Acknowledgements +---------------- + +A draft of this code was written during an REU project at University of +Colorado Boulder. We thank Rachel Castro, Joel Courtney, Thomas Magnuson and +Natalie Schoenhals for their contribution to the project and the code. """ +#***************************************************************************** +# Copyright (C) 2020 Chase Meadors , +# Tianyuan Xu +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.structure.parent import Parent from sage.structure.list_clone import NormalizedClonableList from sage.categories.enumerated_sets import EnumeratedSets @@ -53,11 +71,11 @@ def is_fully_commutative(self): r""" Check if ``self`` is the reduced word of an FC element. - To check if `self` is fully commutative, we use the well-known - characterization that an element `w` in a Coxeter system `(W,S)` is FC - if and only if for every pair of generators `s,t \in S` for which - `m(s,t)>2`, no reduced word of `w` contains the 'braid' word `sts...` of - length `m(s,t)` as a contiguous subword. See [Ste1996]_. + To check if `self` is FC, we use the well-known characterization that + an element `w` in a Coxeter system `(W,S)` is FC if and only if for + every pair of generators `s,t \in S` for which `m(s,t)>2`, no reduced + word of `w` contains the 'braid' word `sts...` of length `m(s,t)` as a + contiguous subword. See [Ste1996]_. :func:`check` is an alias of this method, and is called automatically when an element is created. @@ -112,7 +130,8 @@ def is_fully_commutative(self): """ matrix = self.parent().coxeter_matrix() w = tuple(self) - + + # The following function detects 'braid' words. def contains_long_braid(w): for i in range(0, len(w) - 2): a = w[i] @@ -124,12 +143,15 @@ def contains_long_braid(w): return True return False + # The following function applies a commutation relation on a word. def commute_once(word, i): return word[:i] + (word[i + 1], word[i]) + word[i + 2:] not_fc = ValueError( 'The input is not a reduced word of a fully commutative element.') + # A word is the reduced word of an FC element iff no sequence of + # commutation relations on it yields a word with a 'braid' word: if contains_long_braid(w): raise not_fc else: @@ -201,25 +223,26 @@ def heap(self, **kargs): from any reduced word of `w`. Different reduced words yield isomorphic labeled posets, so the heap is well defined. - Heaps are very useful for visualizing and studying FC elements; see, for - example, [Ste1996]_ and [GX2020]_. + Heaps are very useful for visualizing and studying FC elements; see, + for example, [Ste1996]_ and [GX2020]_. INPUT: - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` - and where each element `i` carries `s_i` as its label. The partial order - `\prec` on the poset is defined by declaring `i\prec j` if `i @@ -777,10 +798,23 @@ class FullyCommutativeElements(Parent): OUTPUT: The class of fully commutative elements in the Coxeter group constructed - from ``data``. This will belong to either the category of infinite - enumerated sets or finite enumerated sets depending on if the group is - FC-finite. - + from ``data``. This will belong to the category of enumerated sets. If the + Coxeter data corresponds to a Cartan type, the category is further refined + to either finite enumerated sets or infinite enumerated sets depending on i + whether the Coxeter group is FC-finite; the refinement is not carried out + if ``data`` is a Coxeter matrix not corresponding to a Cartan type. + + .. NOTE:: + + It would be ideal to implement the aforementioned refinement to finite + and infinite enumerated sets for all possible ``data``, regardless of + whether it corresponds to a Cartan type. Doing so requires determining + if an arbitrary Coxeter matrix corresponds to a Cartan type. It may be + best to address this issue in `sage.combinat.root_system`. On the other + hand, the refinement in the general case may be unnecessary in light of + the fact that Stembridge's classification of FC-finite groups contains + a very small number of easily-recognizable families. + EXAMPLES: Enumerate the FC elements in `A_3` in their Cartier--Foata forms :: @@ -807,11 +841,11 @@ class FullyCommutativeElements(Parent): Count the FC elements in `B_8` :: sage: FCB8 = FullyCommutativeElements(['B', 8]) - sage: len(FCB8) # long time (7 seconds) + sage: len(FCB8) # long time (7 seconds) 14299 - Iterate through the FC elements of length up to 2 in the non-FC-finite group - affine `A_2` :: + Iterate through the FC elements of length up to 2 in the non-FC-finite + group affine `A_2` :: sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() @@ -824,7 +858,8 @@ class FullyCommutativeElements(Parent): sage: FCA3([1,2,1]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative element. + ValueError: The input is not a reduced word of a fully commutative + element. Elements are normalized to Cartier--Foata normal form upon construction :: @@ -845,27 +880,27 @@ def __init__(self, data): self._index_set = sorted(self._matrix.index_set()) + # Start with the category of enumerated sets and refine it to finite or + # infinite enumerated sets for Coxeter groups of Cartan types. + category = EnumeratedSets() - # Start with the category of enumerated sets and see if we can refine it - # to finite or infinite by detecting if the group is FC-finite. - category = EnumeratedSets() + # Finite groups will be FC-finite. if self._matrix.is_finite(): - # Finite groups will be FC-finite. - category = category.Finite() - else: + category = category.Finite() + else: try: - # The only infinite Cartan types that are FC-finite are those - # affine types which correspond to F_5 and E_9 in Stembridge's - # notation, that is affine F4 and affine E8. - cartan_type = self._matrix.coxeter_type().cartan_type() - family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() - if affine: - category = category.Finite() if (family == 'F' and rank == 4) or (family == 'E' and rank == 8) else category.Infinite() - else: - category = category.Infinite() + cartan_type = self._matrix.coxeter_type().cartan_type() family, + rank, affine = cartan_type.type(), cartan_type.rank(), + cartan_type.is_affine() + # The only groups of Cartan types that are infinite but + # FC-finite are affine `F_4` and affine `E_8`, which appear as + # `F_5` and `E_9` in [Ste1996]_. + if affine: + category = category.Finite() if (family == 'F' and rank == 4) or (family == 'E' and rank == 8) else category.Infinite() + else: + category = category.Infinite() except: - # Cannot recognize the matrix as a Cartan type, just stay with - # plain enumerated sets. + # no refinement for groups not corresponding to a Cartan type: pass Parent.__init__(self, category=category) @@ -900,12 +935,11 @@ def coxeter_matrix(self): def index_set(self): r""" - Obtain the set of the generators / simple reflections of the associated + Obtain the set of the generators/simple reflections of the associated Coxeter system. OUTPUT: iterable of integers - EXAMPLES:: sage: FCA3 = FullyCommutativeElements(['A', 3]) @@ -928,13 +962,10 @@ def __iter__(self): empty_word = self.element_class(self, [], check=False) letters = self.index_set() - # In the following, we use a dictionary's keys as a replacement for a - # set. Dictinary keys are guaranteed to be ordered in Python 3.7+, so - # this is an easy way to make this iterator deterministic. - + # To make the iterator deterministic, use a dictionary rather than a + # set, for the keys are then ordered by default by Python 3.7+: recent_words = {empty_word: True} yield empty_word - length = 1 while True: new_words = {} From 2ce06b09d433260073a91b447210d4ee4e5181ea Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 28 Jul 2020 20:46:25 +0000 Subject: [PATCH 126/379] Fix typos --- src/sage/combinat/fully_commutative_elements.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index d8488ffa926..e0fd8b34032 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -889,9 +889,8 @@ def __init__(self, data): category = category.Finite() else: try: - cartan_type = self._matrix.coxeter_type().cartan_type() family, - rank, affine = cartan_type.type(), cartan_type.rank(), - cartan_type.is_affine() + cartan_type = self._matrix.coxeter_type().cartan_type() + family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() # The only groups of Cartan types that are infinite but # FC-finite are affine `F_4` and affine `E_8`, which appear as # `F_5` and `E_9` in [Ste1996]_. From 749647ceb7918c88abed21ae20f9deac7f55299c Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 28 Jul 2020 15:03:51 -0600 Subject: [PATCH 127/379] Add plot in docs --- src/sage/combinat/fully_commutative_elements.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index e0fd8b34032..a197c2f2056 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -293,6 +293,12 @@ def plot_heap(self): sage: FC = FullyCommutativeElements(['B', 5]) sage: FC([3,2,4,3,1]).plot_heap() Graphics object consisting of 15 graphics primitives + + .. PLOT:: + + FC = FullyCommutativeElements(['B', 5]) + g = FC([3,2,4,3,1]).plot_heap() + sphinx_plot(g) """ import sage.plot.all as plot From 9b5a4a39924334d99842be8fc402e29404efb569 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 28 Jul 2020 15:04:13 -0600 Subject: [PATCH 128/379] Clean up main class docs; change NOTE to TODO. --- src/sage/combinat/fully_commutative_elements.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index a197c2f2056..9504268bb8c 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -796,10 +796,9 @@ class FullyCommutativeElements(Parent): INPUT: - - ``data`` -- Coxeter matrix or data describing the Cartan type; the latter - should be formatted in the same way as required by the CoxeterGroup - constructor from - :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup`. + - ``data`` -- CoxeterMatrix, CartanType, or the usual datum that can is + taken in the constructors for these classes (see + :func:`sage.combinat.root_system.coxeter_group.CoxeterGroup`) OUTPUT: @@ -810,13 +809,13 @@ class FullyCommutativeElements(Parent): whether the Coxeter group is FC-finite; the refinement is not carried out if ``data`` is a Coxeter matrix not corresponding to a Cartan type. - .. NOTE:: + .. TODO:: It would be ideal to implement the aforementioned refinement to finite and infinite enumerated sets for all possible ``data``, regardless of whether it corresponds to a Cartan type. Doing so requires determining if an arbitrary Coxeter matrix corresponds to a Cartan type. It may be - best to address this issue in `sage.combinat.root_system`. On the other + best to address this issue in ``sage.combinat.root_system``. On the other hand, the refinement in the general case may be unnecessary in light of the fact that Stembridge's classification of FC-finite groups contains a very small number of easily-recognizable families. From 2856d5125a6bda50900f2a8e0371e8ae9279e76c Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 28 Jul 2020 15:05:25 -0600 Subject: [PATCH 129/379] Run docstring formatters --- .../combinat/fully_commutative_elements.py | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 9504268bb8c..3d204504a61 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -71,10 +71,10 @@ def is_fully_commutative(self): r""" Check if ``self`` is the reduced word of an FC element. - To check if `self` is FC, we use the well-known characterization that - an element `w` in a Coxeter system `(W,S)` is FC if and only if for - every pair of generators `s,t \in S` for which `m(s,t)>2`, no reduced - word of `w` contains the 'braid' word `sts...` of length `m(s,t)` as a + To check if `self` is FC, we use the well-known characterization that an + element `w` in a Coxeter system `(W,S)` is FC if and only if for every + pair of generators `s,t \in S` for which `m(s,t)>2`, no reduced word of + `w` contains the 'braid' word `sts...` of length `m(s,t)` as a contiguous subword. See [Ste1996]_. :func:`check` is an alias of this method, and is called automatically @@ -223,26 +223,25 @@ def heap(self, **kargs): from any reduced word of `w`. Different reduced words yield isomorphic labeled posets, so the heap is well defined. - Heaps are very useful for visualizing and studying FC elements; see, - for example, [Ste1996]_ and [GX2020]_. + Heaps are very useful for visualizing and studying FC elements; see, for + example, [Ste1996]_ and [GX2020]_. INPUT: - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` - and where each element `i` carries `s_i` as its label. The partial - order `\prec` on the poset is defined by declaring `i\prec j` if `i Date: Tue, 28 Jul 2020 15:08:36 -0600 Subject: [PATCH 130/379] Fix bare except --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 3d204504a61..59398109cb2 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -902,7 +902,7 @@ def __init__(self, data): category = category.Finite() if (family == 'F' and rank == 4) or (family == 'E' and rank == 8) else category.Infinite() else: category = category.Infinite() - except: + except AttributeError: # no refinement for groups not corresponding to a Cartan type: pass From df550617f31c70803d26ea2711ae2c9b8cf6326e Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 28 Jul 2020 15:15:40 -0600 Subject: [PATCH 131/379] Remove spaces before code block indicator --- .../combinat/fully_commutative_elements.py | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 59398109cb2..1fb77acd6b2 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -84,7 +84,7 @@ def is_fully_commutative(self): To construct an FC element, first call the parent class FullyCommutativeElements. The parent class contains information about - the Coxeter matrix of the ambient Coxeter system :: + the Coxeter matrix of the ambient Coxeter system:: sage: FC = FullyCommutativeElements(['B', 3]) sage: FC.coxeter_matrix() @@ -92,7 +92,7 @@ def is_fully_commutative(self): [3 1 4] [2 4 1] - We can construct FC elements as follows :: + We can construct FC elements as follows:: sage: FC([]) [] @@ -104,7 +104,7 @@ def is_fully_commutative(self): [3, 2, 3] The output is the normalized form of ``self``, which may be a different - reduced word of the element represented by the input :: + reduced word of the element represented by the input:: sage: FC([3,1]) [1, 3] @@ -114,7 +114,7 @@ def is_fully_commutative(self): True If the input is not the reduced word of an FC element, return a - ValueEror :: + ValueEror:: sage: FC([1,2,1]) Traceback (most recent call last): @@ -180,7 +180,7 @@ def cartier_foata_form(self): EXAMPLES: - The following reduced words express the same FC elements in `B_5` :: + The following reduced words express the same FC elements in `B_5`:: sage: FC = FullyCommutativeElements(['B', 5]) sage: FC([1, 4, 3, 5, 2, 4, 3]) @@ -572,7 +572,7 @@ def still_reduced_fc_after_prepending(self, s): EXAMPLES: - Consider the FC element `w = 12` in the group `B_3` :: + Consider the FC element `w = 12` in the group `B_3`:: sage: FCB3 = FullyCommutativeElements(['B', 3]) sage: FCB3.coxeter_matrix() @@ -581,23 +581,23 @@ def still_reduced_fc_after_prepending(self, s): [2 4 1] sage: w = FCB3([1,2]) - When `s=1`, `sw` is 112, which is not reduced :: + When `s=1`, `sw` is 112, which is not reduced:: sage: w.still_reduced_fc_after_prepending(1) False - When `s=2`, `sw` is 212, which is reduced but not FC :: + When `s=2`, `sw` is 212, which is reduced but not FC:: sage: w.still_reduced_fc_after_prepending(2) False - When `s=31, `sw` is 312, which is reduced and FC :: + When `s=31, `sw` is 312, which is reduced and FC:: sage: w.still_reduced_fc_after_prepending(3) True - More examples :: + More examples:: sage: u = FCB3([3,1,2]) sage: u.still_reduced_fc_after_prepending(1) @@ -719,18 +719,18 @@ def star_operation(self, J, direction, side='left'): EXAMPLES: We will compute all star operations on the following FC element in type - `B_6` relative to `J = \{5, 6\}` :: + `B_6` relative to `J = \{5, 6\}`:: sage: FC = FullyCommutativeElements(['B', 6]) sage: w = FC([1, 6, 2, 5, 4, 6, 5]) Whether and how a left star operations can be applied depend on the - coset decomposition `w = w_J \cdot w^J` :: + coset decomposition `w = w_J \cdot w^J`:: sage: w.coset_decomposition({5, 6}) ([6, 5, 6], [1, 2, 4, 5]) - Only the lower star operation is defined on the left on `w` :: + Only the lower star operation is defined on the left on `w`:: sage: w.star_operation({5,6}, 'upper') @@ -738,12 +738,12 @@ def star_operation(self, J, direction, side='left'): [1, 5, 2, 4, 6, 5] Whether and how a right star operations can be applied depend on the - coset decomposition `w = w^J \cdot w_J` :: + coset decomposition `w = w^J \cdot w_J`:: sage: w.coset_decomposition({5, 6}, side='right') ([1, 6, 2, 5, 4], [6, 5]) - Both types of right star operations on defined for this example :: + Both types of right star operations on defined for this example:: sage: w.star_operation({5, 6}, 'upper', side='right') [1, 6, 2, 5, 4, 6, 5, 6] @@ -821,7 +821,7 @@ class FullyCommutativeElements(Parent): EXAMPLES: - Enumerate the FC elements in `A_3` in their Cartier--Foata forms :: + Enumerate the FC elements in `A_3` in their Cartier--Foata forms:: sage: FCA3 = FullyCommutativeElements(['A', 3]) sage: FCA3.category() @@ -842,14 +842,14 @@ class FullyCommutativeElements(Parent): [1, 2, 3], [2, 1, 3, 2]] - Count the FC elements in `B_8` :: + Count the FC elements in `B_8`:: sage: FCB8 = FullyCommutativeElements(['B', 8]) sage: len(FCB8) # long time (7 seconds) 14299 Iterate through the FC elements of length up to 2 in the non-FC-finite group - affine `A_2` :: + affine `A_2`:: sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() @@ -857,7 +857,7 @@ class FullyCommutativeElements(Parent): sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] - Constructing an element that is not fully commutative throws an error :: + Constructing an element that is not fully commutative throws an error:: sage: FCA3([1,2,1]) Traceback (most recent call last): @@ -865,7 +865,7 @@ class FullyCommutativeElements(Parent): ValueError: The input is not a reduced word of a fully commutative element. - Elements are normalized to Cartier--Foata normal form upon construction :: + Elements are normalized to Cartier--Foata normal form upon construction:: sage: FCA3([2, 3, 1, 2]) [2, 1, 3, 2] @@ -999,14 +999,14 @@ def iterate_to_length(self, length): EXAMPLES: The following example produces all FC elements of length up to 2 in the - group `A_3` :: + group `A_3`:: sage: FCA3 = FullyCommutativeElements(['A', 3]) sage: list(FCA3.iterate_to_length(2)) [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3]] The lists for length 4 and 5 are the same since 4 is the maximum length - of an FC element in `A_3` :: + of an FC element in `A_3`:: sage: list(FCA3.iterate_to_length(4)) [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], [3, 2, @@ -1018,7 +1018,7 @@ def iterate_to_length(self, length): True The following example produces all FC elements of length up to 4 in the - affine Weyl group `\tilde A_2` :: + affine Weyl group `\tilde A_2`:: sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) sage: FCAffineA2.category() From f711d9a0d45761d80271668868c737a5457f3190 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 28 Jul 2020 15:42:04 -0600 Subject: [PATCH 132/379] Change error messages to match Python convention. --- src/sage/combinat/fully_commutative_elements.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 1fb77acd6b2..7bbc3cb0db3 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -119,14 +119,14 @@ def is_fully_commutative(self): sage: FC([1,2,1]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative - element. + ValueError: the input is not a reduced word of a fully commutative + element sage: FC([2,3,2,3]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative - element. + ValueError: the input is not a reduced word of a fully commutative + element """ matrix = self.parent().coxeter_matrix() w = tuple(self) @@ -148,7 +148,7 @@ def commute_once(word, i): return word[:i] + (word[i + 1], word[i]) + word[i + 2:] not_fc = ValueError( - 'The input is not a reduced word of a fully commutative element.') + 'the input is not a reduced word of a fully commutative element') # A word is the reduced word of an FC element iff no sequence of # commutation relations on it yields a word with a 'braid' word: @@ -862,8 +862,8 @@ class FullyCommutativeElements(Parent): sage: FCA3([1,2,1]) Traceback (most recent call last): ... - ValueError: The input is not a reduced word of a fully commutative - element. + ValueError: the input is not a reduced word of a fully commutative + element Elements are normalized to Cartier--Foata normal form upon construction:: @@ -879,7 +879,7 @@ def __init__(self, data): t = CartanType(data) except (TypeError, ValueError): raise ValueError( - 'Input must be a CoxeterMatrix or data describing a Cartan type.') + 'input must be a CoxeterMatrix or data describing a Cartan type') self._matrix = t.coxeter_matrix() self._index_set = sorted(self._matrix.index_set()) From 40614a1556e7c8824876253daffffc02450e5294 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 29 Jul 2020 04:43:14 -0600 Subject: [PATCH 133/379] Remove unused variable --- src/sage/combinat/fully_commutative_elements.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 7bbc3cb0db3..93de4bc2ed5 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -960,8 +960,6 @@ def __iter__(self): word and, if the group is FC-finite, ending with the longest fully commutative element. """ - m = self.coxeter_matrix() - empty_word = self.element_class(self, [], check=False) letters = self.index_set() From 455428fa3a7a60ed33e353e5d78c42e2d210ea42 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 29 Jul 2020 05:02:40 -0600 Subject: [PATCH 134/379] Rearrange some misplaced docs --- .../combinat/fully_commutative_elements.py | 103 +++++++----------- 1 file changed, 41 insertions(+), 62 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 93de4bc2ed5..c2c745ec219 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -79,54 +79,6 @@ def is_fully_commutative(self): :func:`check` is an alias of this method, and is called automatically when an element is created. - - EXAMPLES: - - To construct an FC element, first call the parent class - FullyCommutativeElements. The parent class contains information about - the Coxeter matrix of the ambient Coxeter system:: - - sage: FC = FullyCommutativeElements(['B', 3]) - sage: FC.coxeter_matrix() - [1 3 2] - [3 1 4] - [2 4 1] - - We can construct FC elements as follows:: - - sage: FC([]) - [] - sage: FC([1,2]) - [1, 2] - sage: FC([2,3,2]) - [2, 3, 2] - sage: FC([3,2,3]) - [3, 2, 3] - - The output is the normalized form of ``self``, which may be a different - reduced word of the element represented by the input:: - - sage: FC([3,1]) - [1, 3] - sage: FC([2,3,1]) - [2, 1, 3] - sage: FC([1,3]) == FC([3,1]) - True - - If the input is not the reduced word of an FC element, return a - ValueEror:: - - sage: FC([1,2,1]) - Traceback (most recent call last): - ... - ValueError: the input is not a reduced word of a fully commutative - element - - sage: FC([2,3,2,3]) - Traceback (most recent call last): - ... - ValueError: the input is not a reduced word of a fully commutative - element """ matrix = self.parent().coxeter_matrix() w = tuple(self) @@ -821,7 +773,47 @@ class FullyCommutativeElements(Parent): EXAMPLES: - Enumerate the FC elements in `A_3` in their Cartier--Foata forms:: + Create the enumerate set of fully commutative elements in `B_3`:: + + sage: FC = FullyCommutativeElements(['B', 3]) + sage: FC.coxeter_matrix() + [1 3 2] + [3 1 4] + [2 4 1] + + Construct elements:: + + sage: FC([]) + [] + sage: FC([1,2]) + [1, 2] + sage: FC([2,3,2]) + [2, 3, 2] + sage: FC([3,2,3]) + [3, 2, 3] + + Elements are normalized to Cartier--Foata normal form upon construction:: + + sage: FC([3,1]) + [1, 3] + sage: FC([2,3,1]) + [2, 1, 3] + sage: FC([1,3]) == FC([3,1]) + True + + Attempting to create an element from an input that is not the reduced word + of a fully commutative element throws a ``ValueError``:: + + sage: FC([1,2,1]) + Traceback (most recent call last): + ... + ValueError: the input is not a reduced word of a fully commutative element + sage: FC([2,3,2,3]) + Traceback (most recent call last): + ... + ValueError: the input is not a reduced word of a fully commutative element + + Enumerate the FC elements in `A_3`:: sage: FCA3 = FullyCommutativeElements(['A', 3]) sage: FCA3.category() @@ -856,19 +848,6 @@ class FullyCommutativeElements(Parent): Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] - - Constructing an element that is not fully commutative throws an error:: - - sage: FCA3([1,2,1]) - Traceback (most recent call last): - ... - ValueError: the input is not a reduced word of a fully commutative - element - - Elements are normalized to Cartier--Foata normal form upon construction:: - - sage: FCA3([2, 3, 1, 2]) - [2, 1, 3, 2] """ def __init__(self, data): From 9044364cb8c375ca8c236809466a6c4e60809e01 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 29 Jul 2020 05:03:03 -0600 Subject: [PATCH 135/379] Have .check() not return anything --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index c2c745ec219..d014d8cefdb 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -120,7 +120,7 @@ def commute_once(word, i): else: checked.add(new_word) queue.appendleft(new_word) - return True + return # Representing FC elements: Canonical forms def cartier_foata_form(self): From 191b7ab43b2581c849ac340c2f5c3cab7868563e Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 29 Jul 2020 05:11:32 -0600 Subject: [PATCH 136/379] Refactor .check() and .is_fully_commutative() to be more semantically correct. .is_fully_commutative() now returns a boolean, and .check() raises the ValueError. --- src/sage/combinat/fully_commutative_elements.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index d014d8cefdb..41715a2c746 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -53,7 +53,8 @@ def check(self): Called automatically when an element is created. Alias of :func:`is_fully_commutative` """ - return self.is_fully_commutative() + if not self.is_fully_commutative(): + raise ValueError('the input is not a reduced word of a fully commutative element') def normalize(self): r""" @@ -99,13 +100,10 @@ def contains_long_braid(w): def commute_once(word, i): return word[:i] + (word[i + 1], word[i]) + word[i + 2:] - not_fc = ValueError( - 'the input is not a reduced word of a fully commutative element') - # A word is the reduced word of an FC element iff no sequence of # commutation relations on it yields a word with a 'braid' word: if contains_long_braid(w): - raise not_fc + return False else: l, checked, queue = len(w), {w}, deque([w]) while queue: @@ -116,11 +114,11 @@ def commute_once(word, i): new_word = commute_once(word, i) if new_word not in checked: if contains_long_braid(new_word): - raise not_fc + return False else: checked.add(new_word) queue.appendleft(new_word) - return + return True # Representing FC elements: Canonical forms def cartier_foata_form(self): From dd94864b655ec2297e88b4d725c637add833333a Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 29 Jul 2020 05:25:36 -0600 Subject: [PATCH 137/379] Add _repr_ for parent class --- .../combinat/fully_commutative_elements.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 41715a2c746..9294fdb09f7 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -773,7 +773,8 @@ class FullyCommutativeElements(Parent): Create the enumerate set of fully commutative elements in `B_3`:: - sage: FC = FullyCommutativeElements(['B', 3]) + sage: FC = FullyCommutativeElements(['B', 3]); FC + Fully commutative elements in Coxeter system with Cartan type ['B', 3] sage: FC.coxeter_matrix() [1 3 2] [3 1 4] @@ -885,6 +886,27 @@ def __init__(self, data): Parent.__init__(self, category=category) + def _repr_(self): + r""" + EXAMPLES:: + + sage: FullyCommutativeElements(['H', 4]) + Fully commutative elements in Coxeter system with Cartan type ['H', 4] + sage: m = CoxeterMatrix([(1, 5, 2, 2, 2), (5, 1, 3, 2, 2), (2, 3, 1, 3, 2), (2, 2, 3, 1, 3), (2, 2, 2, 3, 1)]) + sage: FullyCommutativeElements(m) + Fully commutative elements in Coxeter system with Coxeter matrix + [1 5 2 2 2] + [5 1 3 2 2] + [2 3 1 3 2] + [2 2 3 1 3] + [2 2 2 3 1] + """ + try: + ctype = self.coxeter_matrix().coxeter_type().cartan_type() + return 'Fully commutative elements in Coxeter system with Cartan type {}'.format(str(ctype)) + except AttributeError: + return 'Fully commutative elements in Coxeter system with Coxeter matrix\n{}'.format(str(self.coxeter_matrix())) + def _element_constructor_(self, lst): return self.element_class(self, lst) From 42fb3ba13c74147b1b2944c50e84ac6833724771 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 29 Jul 2020 05:25:46 -0600 Subject: [PATCH 138/379] Achieve 100% test coverage --- .../combinat/fully_commutative_elements.py | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 9294fdb09f7..6eefc5c329d 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -52,6 +52,15 @@ def check(self): r""" Called automatically when an element is created. Alias of :func:`is_fully_commutative` + + TESTS:: + + sage: FullyCommutativeElements(['A', 3])([1, 2]) # indirect doctest + [1, 2] + sage: FullyCommutativeElements(['A', 3])([1, 2, 1]) # indirect doctest + Traceback (most recent call last): + ... + ValueError: the input is not a reduced word of a fully commutative element """ if not self.is_fully_commutative(): raise ValueError('the input is not a reduced word of a fully commutative element') @@ -60,6 +69,11 @@ def normalize(self): r""" Called automatically when an element is created. Alias of :func:`cartier_foata_form` + + TESTS:: + + sage: FullyCommutativeElements(['A', 3])([3, 1]) # indirect doctest + [1, 3] """ return self.cartier_foata_form() @@ -80,6 +94,14 @@ def is_fully_commutative(self): :func:`check` is an alias of this method, and is called automatically when an element is created. + + TESTS:: + + sage: FC = FullyCommutativeElements(['A', 3]) + sage: x = FC([1, 2]); x.is_fully_commutative() + True + sage: x = FC.element_class(FC, [1, 2, 1], check=False); x.is_fully_commutative() + False """ matrix = self.parent().coxeter_matrix() w = tuple(self) @@ -133,11 +155,11 @@ def cartier_foata_form(self): The following reduced words express the same FC elements in `B_5`:: sage: FC = FullyCommutativeElements(['B', 5]) - sage: FC([1, 4, 3, 5, 2, 4, 3]) + sage: FC([1, 4, 3, 5, 2, 4, 3]) # indirect doctest [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 1, 3, 5, 2, 4, 3]) + sage: FC([4, 1, 3, 5, 2, 4, 3]) # indirect doctest [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 3, 1, 5, 4, 2, 3]) + sage: FC([4, 3, 1, 5, 4, 2, 3]) # indirect doctest [1, 4, 3, 5, 2, 4, 3] .. NOTE:: @@ -850,6 +872,16 @@ class FullyCommutativeElements(Parent): """ def __init__(self, data): + r""" + EXAMPLES:: + + sage: FullyCommutativeElements(['B', 3]) + Fully commutative elements in Coxeter system with Cartan type ['B', 3] + sage: FullyCommutativeElements(CartanType(['B', 3])) + Fully commutative elements in Coxeter system with Cartan type ['B', 3] + sage: FullyCommutativeElements(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})) + Fully commutative elements in Coxeter system with Cartan type ['B', 3] relabelled by {1: 3, 2: 2, 3: 1} + """ if isinstance(data, CoxeterMatrix): self._matrix = data else: @@ -908,6 +940,13 @@ def _repr_(self): return 'Fully commutative elements in Coxeter system with Coxeter matrix\n{}'.format(str(self.coxeter_matrix())) def _element_constructor_(self, lst): + r""" + TESTS:: + + sage: FC = FullyCommutativeElements(['A', 3]) + sage: FC([1, 2]) # indirect doctest + [1, 2] + """ return self.element_class(self, lst) Element = FullyCommutativeElement @@ -958,6 +997,12 @@ def __iter__(self): Enumerate the elements of this set by length, starting with the empty word and, if the group is FC-finite, ending with the longest fully commutative element. + + TESTS:: + + sage: FC = FullyCommutativeElements(['A', 10]) + sage: next(iter(FC)) # indirect doctest + [] """ empty_word = self.element_class(self, [], check=False) letters = self.index_set() From 10143a3fe946e9e47c7023d842bb9ced936ee758 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Wed, 29 Jul 2020 22:48:51 +0530 Subject: [PATCH 139/379] correct type code removed --- src/sage/graphs/base/boost_graph.pyx | 30 ++-------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index d495e0cd026..6159b70ffbb 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -996,22 +996,11 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): dist = {} pred = {} - if weight_function is not None: - correct_type = type(weight_function(g.edges()[0])) - elif g.weighted(): - correct_type = type(g.edges()[0][2]) - else: - correct_type = int - # Needed for rational curves. - from sage.rings.real_mpfr import RealNumber, RR - if correct_type == RealNumber: - correct_type = RR - import sys for v in range(g.num_verts()): if result.distances[v] != sys.float_info.max: w = int_to_v[v] - dist[w] = correct_type(result.distances[v]) + dist[w] = result.distances[v] pred[w] = int_to_v[result.predecessors[v]] if result.predecessors[v] != v else None return (dist, pred) @@ -2243,21 +2232,6 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if algorithm is None: algorithm = 'Dijkstra' - try: - if weight_function is not None: - correct_type = type(weight_function(g.edges()[0])) - elif g.weighted(): - correct_type = type(g.edges()[0][2]) - else: - correct_type = int - except StopIteration: - correct_type = int - - # Needed for rational curves. - from sage.rings.real_mpfr import RealNumber, RR - if correct_type == RealNumber: - correct_type = RR - cdef list distances = [] cdef list predecessors = [] @@ -2311,7 +2285,7 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al for vert in range(g.num_verts()): if result.distances[vert] != sys.float_info.max: - dist_v.append(correct_type(result.distances[vert])) + dist_v.append(result.distances[vert]) pred = result.predecessors[vert] if pred != vert: pred_v.append(pred) From 1327b8ab41abb05a52d5dabe75aef72e3b37d911 Mon Sep 17 00:00:00 2001 From: Benjamin Hutz Date: Thu, 30 Jul 2020 10:38:04 -0500 Subject: [PATCH 140/379] 29844 doc clean-up --- .../schemes/berkovich/berkovich_cp_element.py | 72 ++++++++++++------- src/sage/schemes/berkovich/berkovich_space.py | 52 +++++++------- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 6802d37bdc2..a0f8cb8bf18 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -58,6 +58,7 @@ class Berkovich_Element(Element): class Berkovich_Element_Cp(Berkovich_Element): r""" The abstract parent class for any element of Berkovich space over `\CC_p`. + This class should never be instantiated, instead use :class:`Berkovich_Element_Cp_Affine` or :class:`Berkovich_Element_Cp_Projective`. @@ -416,7 +417,7 @@ def _custom_abs(self, x): Returns the absolute value of ``x`` with respect to the norm on ``Cp``. Used to simplify code, as ``x`` may be a point of a number field - or a padic field. + or a p-adic field. EXAMPLES:: @@ -500,6 +501,7 @@ def precision(self): """ Returns the precision of a type IV point. + This integer is the number of disks used in the approximation of the type IV point. Not defined for type I, II, or III points. OUTPUT: An integer. @@ -815,7 +817,7 @@ def Hsia_kernel_infinity(self, other): :: sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.ideal(-1/2*a^2 + a - 3) sage: B = Berkovich_Cp_Projective(A, ideal) sage: Q1 = B(4) @@ -850,7 +852,7 @@ def center(self): sage: A. = NumberField(x^3 + 20) sage: ideal = A.ideal(-1/2*a^2 + a - 3) sage: B = Berkovich_Cp_Projective(A, ideal) - sage: B(a^2+4).center() + sage: B(a^2 + 4).center() (a^2 + 4 : 1) """ if self._type == 4: @@ -1005,38 +1007,36 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): Type I points can be created by specifying the corresponding point of ``Cp``:: sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: a = B(4) - sage: a + sage: B(4) Type I point centered at 1 + 3 + O(3^20) The center of a point can be an element of a finite extension of ``Qp``:: sage: A. = Qq(27) - sage: a = B(1+t) - sage: a + sage: B(1 + t) Type I point centered at (t + 1) + O(3^20) Type II and III points can be created by specifying a center and a radius:: - sage: b = B(2, 3**(1/2)); b + sage: B(2, 3**(1/2)) Type II point centered at 2 + O(3^20) of radius 3^1/2 - sage: c = B(2, 1.6); c + sage: B(2, 1.6) Type III point centered at 2 + O(3^20) of radius 1.60000000000000 Some type II points may be mistaken for type III points:: - sage: b = B(3, 3**0.5); b #not tested + sage: B(3, 3**0.5) #not tested Type III point centered at 3 + O(3^21) of radius 1.73205080756888 To avoid these errors, specify the power instead of the radius:: - sage: b = B(3, power=RR(1/100000)); b + sage: B(3, power=RR(1/100000)) Type II point centered at 3 + O(3^21) of radius 3^1/100000 Type IV points can be constructed in a number of ways, the first being from a list of centers and radii used to approximate the point:: - sage: d = B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]); d + sage: B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]) Type IV point of precision 3, approximated by disks centered at [2 + O(3^20), 2 + O(3^20)] ... with radii [1.76100000000000, 1.12300000000000] ... @@ -1044,10 +1044,10 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: A. = Qq(27) sage: R. = PolynomialRing(A) - sage: f = (1+t)^2*x + sage: f = (1 + t)^2*x sage: S. = PolynomialRing(RR) sage: S = FractionField(S) - sage: g = (y+1)/y + sage: g = (y + 1)/y sage: d = B(f, g, prec=100); d Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y @@ -1055,7 +1055,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): For increased performance, error_check can be set to ``False``. WARNING: with error check set to ``False``, any error in the input will lead to incorrect results:: - sage: d = B(f, g, prec=100,error_check=False); d + sage: B(f, g, prec=100,error_check=False) Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y @@ -1070,7 +1070,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): :: - sage: Q2 = B(a+1, 3); Q2 + sage: B(a+1, 3) Type II point centered at (a + 1 : 1) of radius 3^1 TESTS:: @@ -1119,6 +1119,15 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): Type II point centered at w + O(w^41) of radius 5^1 """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): + """ + Initialization function. + + EXAMPLES:: + + sage: A = Berkovich_Cp_Affine(Qp(17)) + sage: A(5, 1) + Type II point centered at 5 + O(17^20) of radius 17^0 + """ #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space #unless we are passed a point of projective Berkovich space Element.__init__(self, parent) @@ -1127,7 +1136,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check self._base_type = parent._base_type self._ideal = parent._ideal - #if this is a point of projective berkovich space, we raise an error + #if this is a point of projective Berkovich space, we raise an error if isinstance(center, Berkovich_Element_Cp_Projective): raise TypeError('use as_affine_point to convert to affine Berkovich space') @@ -1138,7 +1147,7 @@ def as_projective_point(self): r""" Returns the corresponding point of projective Berkovich space. - We identify affine Berkovich space with the subset `P^1_{\text{Berk}}(C_p) - (1 : 0)`. + We identify affine Berkovich space with the subset `P^1_{\text{Berk}}(C_p) - \{(1 : 0)\}`. EXAMPLES:: @@ -1519,7 +1528,7 @@ def involution_map(self): The involution map is the extension of the map ``z |-> 1/z`` on `\CC_p` to Berkovich space. - For affine Berkovich Space, not defined for the type I + For affine Berkovich space, not defined for the type I point centered at 0. If zero is contained in every disk approximating a type IV point, @@ -1860,9 +1869,18 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): + """ + Initialization function. + + EXAMPLES:: + + sage: S = ProjectiveSpace(Qp(7), 1) + sage: P = Berkovich_Cp_Projective(S) + sage: P(0,1) + Type II point centered at (0 : 1 + O(7^20)) of radius 7^0 + """ #if we are given a point of Affine Berkovich Space, we do the conversion #otherwise we call the Berkovich_Element_Cp constructor with space_type="projective" - Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() @@ -1890,8 +1908,10 @@ def as_affine_point(self): :: - sage: B(0, 1).as_affine_point() + sage: Q=B(0, 1).as_affine_point(); Q Type II point centered at 0 of radius 5^0 + sage: Q.parent() + Affine Berkovich line over Cp(5) of precision 20 :: @@ -2233,7 +2253,7 @@ def join(self, other, basepoint=Infinity): TESTS:: - sage: Q4 = B(1/3**8+2, 1) + sage: Q4 = B(1/3**8 + 2, 1) sage: Q2.join(Q4, basepoint=Q1) Type III point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 2.00000000000000 @@ -2255,7 +2275,7 @@ def join(self, other, basepoint=Infinity): Type II point centered at (0 : 1 + O(3^20)) of radius 3^1/2 sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.prime_above(3) sage: C = Berkovich_Cp_Projective(A, ideal) sage: Q10 = C(a, 1/9) @@ -2493,9 +2513,9 @@ def contained_in_interval(self, start, end): TESTS:: sage: B = Berkovich_Cp_Projective(3) - sage: infty = B((1,0)) + sage: infty = B((1, 0)) sage: zero = B(0) - sage: gauss = B(0,1) + sage: gauss = B(0, 1) sage: infty.contained_in_interval(zero, gauss) False @@ -2660,4 +2680,4 @@ def diameter(self, basepoint=Infinity): if basepoint == Infinity: return super().diameter() else: - return self.Hsia_kernel(self, basepoint) \ No newline at end of file + return self.Hsia_kernel(self, basepoint) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index fb5f3410c3d..fabcfc46570 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -90,7 +90,7 @@ def residue_characteristic(self): :: sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.ideal(-1/2*a^2 + a - 3) sage: B = Berkovich_Cp_Affine(A, ideal) sage: B.residue_characteristic() @@ -106,8 +106,8 @@ def is_padic_base(self): OUTPUT: - - ``True`` if this Berkovich space was created with a p-adic field - - ``False`` otherwise + - ``True`` if this Berkovich space was created with a p-adic field. + - ``False`` otherwise. EXAMPLES:: @@ -129,8 +129,8 @@ def is_number_field_base(self): OUTPUT: - - ``True`` if this Berkovich space was created with a number field - - ``False`` otherwise + - ``True`` if this Berkovich space was created with a number field. + - ``False`` otherwise. EXAMPLES:: @@ -164,7 +164,7 @@ def ideal(self): EXAMPLES:: sage: R. = QQ[] - sage: A. = NumberField(z^2+1) + sage: A. = NumberField(z^2 + 1) sage: ideal = A.prime_above(5) sage: B = Berkovich_Cp_Projective(A, ideal) sage: B.ideal() @@ -179,8 +179,8 @@ def ideal(self): :: sage: B = Berkovich_Cp_Projective(Qp(3)) - sage: print(B.ideal()) - None + sage: B.ideal() is None + True """ return self._ideal @@ -199,9 +199,9 @@ def __eq__(self,right): :: sage: R. = QQ[] - sage: A. = NumberField(x^2+1) + sage: A. = NumberField(x^2 + 1) sage: A_ideal = A.prime_above(2) - sage: B. = NumberField(x^4+1) + sage: B. = NumberField(x^4 + 1) sage: B_ideal = B.prime_above(2) sage: C = Berkovich_Cp_Projective(A, A_ideal) sage: D = Berkovich_Cp_Projective(B, B_ideal) @@ -276,14 +276,14 @@ class Berkovich_Cp_Affine(Berkovich_Cp): We can create elements:: - sage: Q1 = B(-2); Q1 + sage: B(-2) Type I point centered at 1 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + 2*3^14 + 2*3^15 + 2*3^16 + 2*3^17 + 2*3^18 + 2*3^19 + O(3^20) :: - sage: Q2 = B(1, 2); Q2 + sage: B(1, 2) Type III point centered at 1 + O(3^20) of radius 2.00000000000000 For details on element creation, see the documentation @@ -298,12 +298,12 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: B = Berkovich_Cp_Affine(Qp(3, 1)); B Affine Berkovich line over Cp(3) of precision 1 - sage: Q2 = B(1/2); Q2 + sage: B(1/2) Type I point centered at 2 + O(3) Note that this point has very low precision, as ``B`` was initialized - with a padic field of capped-relative precision one. For high precision, - pass in a high precision padic field:: + with a p-adic field of capped-relative precision one. For high precision, + pass in a high precision p-adic field:: sage: B = Berkovich_Cp_Affine(Qp(3, 1000)); B Affine Berkovich line over Cp(3) of precision 1000 @@ -312,7 +312,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): extensions of `\QQ_p`:: sage: B = Berkovich_Cp_Affine(3) - sage: A. = Qp(3).extension(x^3-3) + sage: A. = Qp(3).extension(x^3 - 3) sage: B(a) Type I point centered at a + O(a^61) @@ -325,7 +325,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): Affine Berkovich line over Cp(3), with base Number Field in a with defining polynomial x^3 + 20 - Number fields a major advantage of exact computation. + Number fields have a major advantage of exact computation. Number fields also have added functionality. Arbitrary extensions of `\QQ` are supported, while there is currently limited functionality @@ -401,7 +401,7 @@ def _repr_(self): sage: R. = QQ[] sage: A. = NumberField(z^2 + 1) sage: ideal = A.prime_above(3) - sage: B = Berkovich_Cp_Affine(A, ideal); B + sage: Berkovich_Cp_Affine(A, ideal) Affine Berkovich line over Cp(3), with base Number Field in a with defining polynomial z^2 + 1 """ @@ -466,7 +466,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Type I point centered at (2 + 3 + 3^2 + 3^3 + 3^4 + 3^5 + 3^6 + 3^7 + 3^8 + 3^9 + 3^10 + 3^11 + 3^12 + 3^13 + 3^14 + 3^15 + 3^16 + 3^17 + 3^18 + 3^19 + O(3^20) : 1 + O(3^20)) - + :: sage: B(2, 1) @@ -474,7 +474,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): For details about element construction, see the documentation of :class:`Berkovich_Element_Cp_Projective`. Initializing a Berkovich projective - line by passing in a padic space looks the same:: + line by passing in a p-adic space looks the same:: sage: B = Berkovich_Cp_Projective(Qp(3)); B Projective Berkovich line over Cp(3) of precision 20 @@ -494,7 +494,7 @@ class Berkovich_Cp_Projective(Berkovich_Cp): a number field, as long as an ideal is specified:: sage: R. = QQ[] - sage: A. = NumberField(x^2+1) + sage: A. = NumberField(x^2 + 1) sage: ideal = A.prime_above(2) sage: B = Berkovich_Cp_Projective(A, ideal); B Projective Berkovich line over Cp(2), with base @@ -592,7 +592,7 @@ def base_ring(self): :: sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.prime_above(3) sage: D = Berkovich_Cp_Projective(A, ideal) sage: D.base_ring() @@ -613,9 +613,9 @@ def _repr_(self): :: sage: R. = QQ[] - sage: A. = NumberField(x^2+1) - sage: v = A.ideal(a+1) - sage: B = Berkovich_Cp_Projective(A, v); B + sage: A. = NumberField(x^2 + 1) + sage: v = A.ideal(a + 1) + sage: Berkovich_Cp_Projective(A, v) Projective Berkovich line over Cp(2), with base Number Field in a with defining polynomial x^2 + 1 """ if self._base_type == 'padic field': @@ -635,4 +635,4 @@ def _latex_(self): sage: latex(B) \text{Projective Berkovich line over } \Bold{C}_{3} """ - return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) \ No newline at end of file + return r"\text{Projective Berkovich line over } \Bold{C}_{%s}" %(self.prime()) From eaa636c3ca68fdfce655a7e3d8ebd31d9433449b Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 30 Jul 2020 14:15:11 -0400 Subject: [PATCH 141/379] 29844: deleted duplicate functions. added element file to docs --- src/doc/en/reference/schemes/index.rst | 1 + .../schemes/berkovich/berkovich_cp_element.py | 410 ++++++------------ 2 files changed, 131 insertions(+), 280 deletions(-) diff --git a/src/doc/en/reference/schemes/index.rst b/src/doc/en/reference/schemes/index.rst index 1f4a64f0c7c..ef2f2595104 100644 --- a/src/doc/en/reference/schemes/index.rst +++ b/src/doc/en/reference/schemes/index.rst @@ -98,6 +98,7 @@ Berkovich Analytic Space .. toctree:: :maxdepth: 1 + sage/schemes/berkovich/berkovich_cp_element sage/schemes/berkovich/berkovich_space .. include:: ../footer.txt diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index a0f8cb8bf18..60eb76e41f0 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -579,17 +579,21 @@ def radius(self): return self._radius_lst return self._radius - def diameter(self): - """ - Diameter function on Berkovich space. + def diameter(self, basepoint=Infinity): + r""" + Generalized diameter function on Berkovich space. - For type I, II, and III points, returns the radius. + If the basepoint is infinity, the diameter is equal to + the limit of the radii of the corresponding disks in `\CC_p`. - For type IV points returns either the last radius - in the finite approximation, or if a generating function - was given for the radii, the diameter is computed - as the limit of the function as it's variable tends - to infinity. + If the basepoint is not infinity, the diameter + is the Hsia kernel of this point with itself at + basepoint ``basepoint``. + + INPUT: + + - ``basepoint`` -- (default = Infinity) A point of the + same Berkovich space as this point. OUTPUT: A real number. @@ -615,22 +619,41 @@ def diameter(self): sage: g = (y+1)/y sage: B(f,g).diameter() 1.0 + + :: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(1/81, 1) + sage: Q2 = B(1/3) + sage: Q1.diameter(Q2) + 0.00137174211248285 + + :: + + sage: Q2.diameter(Q2) + +infinity """ - if self._type == 4: - if self._radius_func == None: - return self._radius_lst[-1] - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(QQ, names="x") - x = R.gens()[0] - if is_Expression(self._radius_func): - radius_func_variable = self._radius_func.variables()[0] - radius_expr = self._radius_func.subs({radius_func_variable:x}) - else: - radius_expr = self._radius_func(x) - from sage.symbolic.ring import SymbolicRing as SR - radius_expr = SR(RR)(radius_expr) - return radius_expr.limit(x="oo") - return self._radius + if basepoint == Infinity: + if self._type == 4: + if self._radius_func == None: + return self._radius_lst[-1] + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(QQ, names="x") + x = R.gens()[0] + if is_Expression(self._radius_func): + radius_func_variable = self._radius_func.variables()[0] + radius_expr = self._radius_func.subs({radius_func_variable:x}) + else: + radius_expr = self._radius_func(x) + from sage.symbolic.ring import SymbolicRing as SR + radius_expr = SR(RR)(radius_expr) + return radius_expr.limit(x="oo") + return self._radius + if not isinstance(basepoint, Berkovich_Element_Cp): + raise TypeError('basepoint must be a point of Berkovich space, not %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError('basepoint must be a point of the same Berkovich space') + return self.Hsia_kernel(self, basepoint) def path_distance_metric(self, other): r""" @@ -793,6 +816,89 @@ def small_metric(self, other): return 2*(new_self.join(new_other,gauss).diameter()) \ - new_self.diameter() - new_other.diameter() + def potential_kernel(self, other, basepoint): + """ + The potential kernel of this point with ``other``, + with basepoint ``basepoint``. + + The potential kernel is the hyperbolic distance + between ``basepoint`` and the join of this point + with ``other`` relative to ``basepoint``. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + - ``basepoint`` -- A point of the same Berkovich space as this point. + + OUTPUT: A finite or infinite real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(27, 1) + sage: Q2 = B(1/3, 2) + sage: Q3 = B(1/9, 1/2) + sage: Q3.potential_kernel(Q1, Q2) + 0.369070246428543 + + :: + + sage: B = Berkovich_Cp_Affine(3) + sage: Q1 = B(27, 1) + sage: Q2 = B(1/3, 2) + sage: Q3 = B(1/9, 1/2) + sage: Q3.potential_kernel(Q1, Q2) + 0.369070246428543 + """ + if not isinstance(other, type(self)): + raise TypeError('other must be a point of a Berkovich space, not %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same Berkovich space') + if not isinstance(basepoint, type(self)): + raise TypeError('basepoint must be a point of Berkovich line, not %s' %basepoint) + if basepoint.parent() != self.parent(): + raise ValueError('basepoint must be a point of the same Berkovich space') + return basepoint.path_distance_metric(self.join(other, basepoint)) + + def spherical_kernel(self,other): + r""" + The spherical kernel of this point with ``other``. + + The spherical kernel is one possible extension of the spherical + distance on `P^1(\CC_p)` to the projective Berkovich line. + See [BR2010]_ for details. + + INPUT: + + - ``other`` -- A point of the same Berkovich space as this point. + + OUTPUT: A real number. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(2, 2) + sage: Q2 = B(1/9, 1) + sage: Q1.spherical_kernel(Q2) + 0.500000000000000 + + :: + + sage: Q3 = B(2) + sage: Q3.spherical_kernel(Q3) + 0 + """ + if not isinstance(other, type(self)): + raise TypeError('other must be a point of Berkovich space, not %s' %other) + if other.parent() != self.parent(): + raise ValueError('other must be a point of the same Berkovich space') + gauss_point = self.parent()(ZZ(0), ZZ(1)) + w = self.join(other,gauss_point) + dist = gauss_point.path_distance_metric(w) + if dist == Infinity: + return 0 + return self.prime()**(-1*dist) + def Hsia_kernel_infinity(self, other): r""" Return the Hsia kernel at infinity of this point with ``other``. @@ -1191,30 +1297,6 @@ def as_projective_point(self): radius = self.radius_function() return new_space(center, radius, prec=self.prec()) - def center(self): - """ - Returns the center of the corresponding disk (or sequence of disks) in ``Cp``. - - OUTPUT: - - - For type I-III points, a point of ``Cp``. - - For type IV points, a list of points of ``Cp``. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(2, 1) - sage: Q1.center() - 2 + O(3^20) - - :: - - sage: d = B([4, 2], [4, 2]) - sage: d.center() - [1 + 3 + O(3^20), 2 + O(3^20)] - """ - return super().center() - def __eq__(self, other): """ Equality operator. @@ -1662,104 +1744,6 @@ def contained_in_interval(self, start, end): proj_end = end.as_projective_point() return proj_self.contained_in_interval(proj_start, proj_end) - def potential_kernel(self, other, basepoint): - """ - The potential kernel of this point with ``other`` with basepoint ``basepoint``. - - The potential kernel is the hyperbolic distance between ``basepoint`` and the join - of this point with ``other`` relative to ``basepoint``. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- A point of the same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(27, 1) - sage: Q2 = B(1/3, 2) - sage: Q3 = B(1/9, 1/2) - sage: Q3.potential_kernel(Q1, Q2) - 0.369070246428543 - """ - if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other must be a point of the same affine Berkovich space') - if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise TypeError('basepoint must be a point of affine Berkovich space. basepoint was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError('basepoint must be a point of the same affine Berkovich space') - return basepoint.path_distance_metric(self.join(other, basepoint)) - - def spherical_kernel(self,other): - r""" - The spherical kernel of this point with ``other``. - - The spherical kernel is one possible extension of - the spherical distance on `A^1(\CC_p)` to the Berkovich - Affine line. See [BR2010]_ for details. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(Qp(3)) - sage: Q1 = B(2, 9) - sage: Q2 = B(1/27, 1/27) - sage: Q1.spherical_kernel(Q2) - 0.111111111111111 - - """ - if not isinstance(other,Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of an affine Berkovich space. other was %s' %other) - if self.parent() != other.parent(): - raise ValueError('other was not a point of the same affine Berkovich space') - gauss_point = self.parent()(RR(0), RR(1)) - w = self.join(other,gauss_point) - dist = gauss_point.path_distance_metric(w) - if dist == Infinity: - return 0 - return (self.prime())**(-1*dist) - - def diameter(self, basepoint=Infinity): - r""" - Generalized diameter function. - - If the basepoint is infinity, the diameter is equal to - the limit of the radii of the corresponding disks in `\CC_p`. - - If the basepoint is not infinity, the diameter - is the Hsia kernel of this point with itself at - basepoint ``basepoint``. - - INPUT: - - - ``basepoint`` -- (default = Infinity) A point of the - same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Affine(3) - sage: Q1 = B(1/81, 1) - sage: Q2 = B(1/3) - sage: Q1.diameter(Q2) - 0.00137174211248285 - - :: - - sage: Q2.diameter(Q2) - +infinity - """ - if basepoint == Infinity: - return super().diameter() - return self.Hsia_kernel(self, basepoint) - class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" Element class of the Berkovich projective line over `\CC_p`. @@ -1948,30 +1932,6 @@ def as_affine_point(self): radius = self.radius_function() return new_space(center, radius, prec=self.prec()) - def center(self): - r""" - Returns the center of the corresponding disk (or sequence of disks) in `P^1(\CC_p)`. - - OUTPUT: - - - For type I-III points, a point of `P^1(\CC_p)`. - - For type IV points, a list of points of `P^1(\CC_p)`. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 1) - sage: Q1.center() - (2 + O(3^20) : 1 + O(3^20)) - - :: - - sage: d = B([4, 2], [4, 2]) - sage: d.center() - [(1 + 3 + O(3^20) : 1 + O(3^20)), (2 + O(3^20) : 1 + O(3^20))] - """ - return super().center() - def __eq__(self, other): """ Equality operator. @@ -2571,113 +2531,3 @@ def contained_in_interval(self, start, end): s_ge_start = self.gt(start) or self == start s_ge_end = self.gt(end) or self == end return j_ge_s and (s_ge_end or s_ge_start) - - def potential_kernel(self, other, basepoint): - """ - The potential kernel of this point with ``other``, - with basepoint ``basepoint``. - - The potential kernel is the hyperbolic distance - between ``basepoint`` and the join of this point - with ``other`` relative to ``basepoint``. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - ``basepoint`` -- A point of the same Berkovich space as this point. - - OUTPUT: A finite or infinite real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(27, 1) - sage: Q2 = B(1/3, 2) - sage: Q3 = B(1/9, 1/2) - sage: Q3.potential_kernel(Q1, Q2) - 0.369070246428543 - """ - if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) - if other.parent() != self.parent(): - raise ValueError('other must be a point of the same projective Berkovich line') - if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) - if basepoint.parent() != self.parent(): - raise ValueError('basepoint must be a point of the same projective Berkovich line') - return basepoint.path_distance_metric(self.join(other, basepoint)) - - def spherical_kernel(self,other): - r""" - The spherical kernel of this point with ``other``. - - The spherical kernel is one possible extension of the spherical - distance on `P^1(\CC_p)` to the projective Berkovich line. - See [BR2010]_ for details. - - INPUT: - - - ``other`` -- A point of the same Berkovich space as this point. - - OUTPUT: A real number. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(2, 2) - sage: Q2 = B(1/9, 1) - sage: Q1.spherical_kernel(Q2) - 0.500000000000000 - - :: - - sage: Q3 = B(2) - sage: Q3.spherical_kernel(Q3) - 0 - """ - if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) - if other.parent() != self.parent(): - raise ValueError('other must be a point of the same projective Berkovich line') - gauss_point = self.parent()(ZZ(0), ZZ(1)) - w = self.join(other,gauss_point) - dist = gauss_point.path_distance_metric(w) - if dist == Infinity: - return 0 - return self.prime()**(-1*dist) - - def diameter(self, basepoint=Infinity): - r""" - Generalized diameter function. - - If the basepoint is infinity, the diameter is equal to - the limit of the radii of the corresponding disks in `\CC_p`. - - If the basepoint is not infinity, the diameter - is the Hsia kernel of this point with itself at - basepoint ``basepoint``. - - INPUT: - - - ``basepoint`` -- (default = Infinity) A point of the same - Berkovich space as this point, or infinity. - - OUTPUT: A real number or infinity. - - EXAMPLES:: - - sage: B = Berkovich_Cp_Projective(3) - sage: Q1 = B(1/81, 1) - sage: Q2 = B(1/3) - sage: Q1.diameter(Q2) - 0.00137174211248285 - - :: - - sage: Q2.diameter(Q2) - +infinity - """ - if basepoint == Infinity: - return super().diameter() - else: - return self.Hsia_kernel(self, basepoint) From b72d178ac5e541ba9413a2d5c932af73234d9124 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 30 Jul 2020 14:33:30 -0400 Subject: [PATCH 142/379] 29844: fixed parent error in involution map --- src/sage/schemes/berkovich/berkovich_cp_element.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 60eb76e41f0..6c2048e080b 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -1,7 +1,7 @@ r""" Elements of Berkovich space. -:class:`Berkovich_Element` is abstract parent class for elements of any Berkovich space. +:class:`Berkovich_Element` is an abstract parent class for elements of any Berkovich space. :class:`Berkovich_Element_Cp_Affine` and :class:`Berkovich_Element_Cp_Projective` implement elements of Berkovich space over `\CC_p` and `P^1(\CC_p)`. Elements are @@ -1673,7 +1673,7 @@ def involution_map(self): raise ValueError("involution map not deffined on affine type I point centered at 0") return self.parent()(1/self.center()) - zero = self.parent()(QQ(0)) + zero = self.parent()(ZZ(0)) radius = self.radius() if self.type_of_point() in [2,3]: @@ -1681,9 +1681,9 @@ def involution_map(self): if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() - return self.parent()(0, power=-power) - return self.parent()(0, RR(1/radius)) - return self.parent()(1/self.center(), RR( radius / (self._custom_abs(self.center())**2) ) ) + return self.parent()(ZZ(0), power=-power) + return self.parent()(ZZ(0), RR(1/radius)) + return self.parent()(1/self.center(), RR(radius / (self._custom_abs(self.center())**2))) new_center_lst = [] new_radius_lst = [] @@ -2421,8 +2421,8 @@ def involution_map(self): if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() - return self.parent()(0, power=-power) - return self.parent()(0, 1/self.radius()) + return self.parent()(ZZ(0), power=-power) + return self.parent()(ZZ(0), 1/self.radius()) return self.parent()(1/self.center()[0], self.radius()/(self._custom_abs(self.center()[0])**2)) new_center_lst = [] From c1becdb7ffcf29cade312aa6350716f220f29317 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Thu, 30 Jul 2020 15:22:07 -0400 Subject: [PATCH 143/379] 29844: minor doc updates --- .../schemes/berkovich/berkovich_cp_element.py | 104 ++++++++++-------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 6c2048e080b..a9778017857 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -379,7 +379,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if radius != None: if is_Expression(radius): try: - power = QQ((radius.log(self._p)).expand_log()) + power = QQ(radius.log(self._p).expand_log()) except TypeError: pass try: @@ -410,7 +410,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= self._power = power return - raise TypeError('unknown error constructing point of Berkovich space over Cp') + raise ValueError('unknown error constructing point of Berkovich space over Cp') def _custom_abs(self, x): """ @@ -554,12 +554,12 @@ def power(self): return self._power def radius(self): - """ - Radius of the corresponding disk (or sequence of disks) in ``Cp``. + r""" + Radius of the corresponding disk (or sequence of disks) in `\CC_p`. OUTPUT: - - A non-negative real number for points Types I-III. + - A non-negative real number for type I, II, or III points. - A list of non-negative real numbers for type IV points. EXAMPLES:: @@ -599,7 +599,7 @@ def diameter(self, basepoint=Infinity): EXAMPLES:: - sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: B = Berkovich_Cp_Affine(3) sage: Q1 = B(3) sage: Q1.diameter() 0 @@ -674,7 +674,7 @@ def path_distance_metric(self, other): EXAMPLES:: - sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: B = Berkovich_Cp_Affine(3) sage: Q1 = B(1/4, 4) sage: Q2 = B(1/4, 6) sage: Q1.path_distance_metric(Q2) @@ -700,8 +700,8 @@ def path_distance_metric(self, other): return 0 else: return RR(Infinity) - return 2*(self.join(other).diameter().log(self.prime()))\ - - self.diameter().log(self.prime())\ + return 2*self.join(other).diameter().log(self.prime()) \ + - self.diameter().log(self.prime()) \ - other.diameter().log(other.prime()) big_metric = path_distance_metric @@ -753,7 +753,7 @@ def Hsia_kernel(self, other, basepoint): if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return RR(Infinity) - return (self.spherical_kernel(other))/ \ + return self.spherical_kernel(other)/ \ (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) def small_metric(self, other): @@ -771,7 +771,7 @@ def small_metric(self, other): EXAMPLES:: - sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: B = Berkovich_Cp_Affine(3) sage: Q1 = B(1/4, 4) sage: Q2 = B(1/4, 6) sage: Q1.small_metric(Q2) @@ -804,7 +804,7 @@ def small_metric(self, other): g_greater_than_s = gauss.gt(self) g_greater_than_o = gauss.gt(other) if g_greater_than_s and g_greater_than_o: - return 2*(self.join(other,gauss).diameter()) - self.diameter() - other.diameter() + return 2*self.join(other, gauss).diameter() - self.diameter() - other.diameter() if not g_greater_than_s: new_self = self.involution_map() else: @@ -813,7 +813,7 @@ def small_metric(self, other): new_other = other.involution_map() else: new_other = other - return 2*(new_self.join(new_other,gauss).diameter()) \ + return 2*new_self.join(new_other, gauss).diameter() \ - new_self.diameter() - new_other.diameter() def potential_kernel(self, other, basepoint): @@ -893,7 +893,7 @@ def spherical_kernel(self,other): if other.parent() != self.parent(): raise ValueError('other must be a point of the same Berkovich space') gauss_point = self.parent()(ZZ(0), ZZ(1)) - w = self.join(other,gauss_point) + w = self.join(other, gauss_point) dist = gauss_point.path_distance_metric(w) if dist == Infinity: return 0 @@ -966,8 +966,8 @@ def center(self): return self._center def type_of_point(self): - """ - Returns the type of this point of Berkovich space over ``Cp`` + r""" + Returns the type of this point of Berkovich space over `\CC_p`. OUTPUT: An integer between 1 and 4 inclusive. @@ -1018,7 +1018,7 @@ def _repr_(self): EXAMPLES:: - sage: B = Berkovich_Cp_Projective((3)) + sage: B = Berkovich_Cp_Projective(3) sage: B(2, 1) Type II point centered at (2 + O(3^20) : 1 + O(3^20)) of radius 3^0 """ @@ -1048,7 +1048,7 @@ def _latex_(self): EXAMPLES:: - sage: B = Berkovich_Cp_Projective((3)) + sage: B = Berkovich_Cp_Projective(3) sage: latex(B(2, 1)) \text{type 2 Point of } \text{Projective Berkovich line over } \Bold{C}_{3} \text{equivalent to the disk centered at @@ -1126,6 +1126,9 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: B(2, 3**(1/2)) Type II point centered at 2 + O(3^20) of radius 3^1/2 + + :: + sage: B(2, 1.6) Type III point centered at 2 + O(3^20) of radius 1.60000000000000 @@ -1161,7 +1164,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): For increased performance, error_check can be set to ``False``. WARNING: with error check set to ``False``, any error in the input will lead to incorrect results:: - sage: B(f, g, prec=100,error_check=False) + sage: B(f, g, prec=100, error_check=False) Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y @@ -1176,12 +1179,12 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): :: - sage: B(a+1, 3) + sage: B(a + 1, 3) Type II point centered at (a + 1 : 1) of radius 3^1 TESTS:: - sage: A = Berkovich_Cp_Affine(Qp(3)) + sage: A = Berkovich_Cp_Affine(3) sage: Q1 = A(3, 1); Q1 Type II point centered at 3 + O(3^21) of radius 3^0 @@ -1208,7 +1211,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: Q4 = A(3, 3**0); Q4 Type II point centered at 3 + O(3^21) of radius 3^0 - sage: Q5 = A(3, power = 1/2); Q5 + sage: Q5 = A(3, power=1/2); Q5 Type II point centered at 3 + O(3^21) of radius 3^1/2 sage: Q6 = A(3, RR(3**(1/2))); Q6 @@ -1220,7 +1223,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: k = Qp(5) sage: R. = k[] sage: l. = k.extension(x^2 - 5) - sage: B = Berkovich_Cp_Affine(Qp(5)) + sage: B = Berkovich_Cp_Affine(5) sage: B(w, power=1) Type II point centered at w + O(w^41) of radius 5^1 """ @@ -1230,7 +1233,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check EXAMPLES:: - sage: A = Berkovich_Cp_Affine(Qp(17)) + sage: A = Berkovich_Cp_Affine(17) sage: A(5, 1) Type II point centered at 5 + O(17^20) of radius 17^0 """ @@ -1339,7 +1342,7 @@ def __eq__(self, other): return self.center() == other.center() elif stype == otype and stype == 4: raise NotImplementedError("Equality for type IV points not yet implemented") - elif stype in [2,3] and otype in [2,3]: + elif stype in [2, 3] and otype in [2, 3]: if self.radius() != other.radius(): return False center_dist = self._custom_abs(self.center() - other.center()) @@ -1543,7 +1546,7 @@ def join(self, other, basepoint=Infinity): EXAMPLES:: - sage: B = Berkovich_Cp_Affine(Qp(3)) + sage: B = Berkovich_Cp_Affine(3) sage: Q1 = B(2, 1) sage: Q2 = B(2, 2) sage: Q1.join(Q2) @@ -1623,7 +1626,7 @@ def involution_map(self): The involution map is 1/z on type I points:: - sage: B = Berkovich_Cp_Affine((3)) + sage: B = Berkovich_Cp_Affine(3) sage: Q1 = B(1/2) sage: Q1.involution_map() Type I point centered at 2 + O(3^20) @@ -1655,8 +1658,7 @@ def involution_map(self): :: - sage: Q1 = B([1, 2], [3, 1]) - sage: Q1.involution_map() + sage: B([1, 2], [3, 1]).involution_map() Traceback (most recent call last): ... ValueError: precision of type IV is not high enough to define image @@ -1800,10 +1802,14 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: P = Berkovich_Cp_Projective(S); P Projective Berkovich line over Cp(5) of precision 20 - sage: a = S(0,1) + :: + + sage: a = S(0, 1) sage: Q1 = P(a); Q1 Type I point centered at (0 : 1 + O(5^20)) + :: + sage: Q2 = P((1,0)); Q2 Type I point centered at (1 + O(5^20) : 0) @@ -1812,6 +1818,8 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: Q3 = P((0,5), 5**(3/2)); Q3 Type II point centered at (0 : 1 + O(5^20)) of radius 5^3/2 + :: + sage: Q4 = P(0, 3**(3/2)); Q4 Type III point centered at (0 : 1 + O(5^20)) of radius 5.19615242270663 @@ -1875,8 +1883,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check if isinstance(center, Berkovich_Element_Cp_Affine): raise TypeError('use as_projective_point to convert to projective Berkovich space') - Berkovich_Element_Cp.__init__(self,parent=parent,center=center,radius=radius,power=power,\ - prec=prec,space_type="projective",error_check=error_check) + Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, \ + prec=prec, space_type="projective", error_check=error_check) def as_affine_point(self): """ @@ -1892,7 +1900,7 @@ def as_affine_point(self): :: - sage: Q=B(0, 1).as_affine_point(); Q + sage: Q = B(0, 1).as_affine_point(); Q Type II point centered at 0 of radius 5^0 sage: Q.parent() Affine Berkovich line over Cp(5) of precision 20 @@ -1914,7 +1922,7 @@ def as_affine_point(self): raise ValueError('cannot convert infinity to affine Berkovich space') from sage.schemes.berkovich.berkovich_space import Berkovich_Cp_Affine new_space = Berkovich_Cp_Affine(self.parent().base_ring(), self.parent().ideal()) - if self.type_of_point() in [1,2,3]: + if self.type_of_point() in [1, 2, 3]: center = self.center()[0] if self.type_of_point() == 1: return new_space(center) @@ -1992,18 +2000,18 @@ def __hash__(self): sage: B = Berkovich_Cp_Projective(3) sage: P = ProjectiveSpace(B.base_ring(), 1) - sage: Q1 = B(P.point([2,2], False), RR(3**(1/2))) - sage: Q2 = B([1,1], 3**(1/2)) + sage: Q1 = B(P.point([2, 2], False), RR(3**(1/2))) + sage: Q2 = B([1, 1], 3**(1/2)) sage: hash(Q1) == hash(Q2) True :: sage: R. = QQ[] - sage: A. = NumberField(x^3+20) + sage: A. = NumberField(x^3 + 20) sage: ideal = A.ideal(-1/2*a^2 + a - 3) sage: B = Berkovich_Cp_Projective(A, ideal) - sage: Q1 = B(a^2+1, 2) + sage: Q1 = B(a^2 + 1, 2) sage: Q2 = B(0, 2) sage: hash(Q1) == hash(Q2) True @@ -2019,7 +2027,7 @@ def lt(self, other): Returns ``True`` if this point is less than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of - the corresponding disks in ``Cp``. + the corresponding disks in `\CC_p`. For example, let x and y be points of type II or III. If x has center `c_1` and radius `r_1` and y has center @@ -2109,12 +2117,12 @@ def gt(self, other): Returns ``True`` if this point is greater than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of - the corresponding disks in ``Cp``. + the corresponding disks in `\CC_p`. For example, let x and y be points of type II or III. If x has center `c_1` and radius `r_1` and y has center - `c_2` and radius `r_2`, `x < y` if and only if `D(c_1,r_1)` - is a subset of `D(c_2,r_2)` in `\CC_p`. + `c_2` and radius `r_2`, `x < y` if and only if `D(c_1, r_1)` + is a subset of `D(c_2, r_2)` in `\CC_p`. INPUT: @@ -2194,7 +2202,7 @@ def join(self, other, basepoint=Infinity): EXAMPLES:: - sage: B = Berkovich_Cp_Projective(ProjectiveSpace(Qp(3), 1)) + sage: B = Berkovich_Cp_Projective(3) sage: Q1 = B(2, 1) sage: Q2 = B(2, 2) sage: Q1.join(Q2) @@ -2257,11 +2265,11 @@ def join(self, other, basepoint=Infinity): if self.type_of_point() == 4: new_center = self.center()[-1] new_radius = self.radius()[-1] - return self.parent()(new_center,new_radius).join(other) + return self.parent()(new_center, new_radius).join(other) if other.type_of_point() == 4: new_center = other.center()[-1] new_radius = other.radius()[-1] - return self.join(self.parent()(new_center,new_radius)) + return self.join(self.parent()(new_center, new_radius)) #we deal with the point at infinity as a special case infty = self.parent()((1,0)) @@ -2339,7 +2347,7 @@ def join(self, other, basepoint=Infinity): #join is symmetric, so we flip self and other so that self > other else: - return other.join(self,basepoint) + return other.join(self, basepoint) def involution_map(self): r""" @@ -2522,8 +2530,8 @@ def contained_in_interval(self, start, end): if self == zero: return end == zero or start == zero if start == zero or end == zero: - gauss = self.parent()(ZZ(0),ZZ(1)) - return self.contained_in_interval(start,gauss) or self.contained_in_interval(gauss,end) + gauss = self.parent()(ZZ(0), ZZ(1)) + return self.contained_in_interval(start, gauss) or self.contained_in_interval(gauss, end) return self.involution_map().contained_in_interval(start.involution_map(), \ end.involution_map()) join = start.join(end) From da4d47d4ac7deb2fdf508ad880ff25b88665dc5c Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 14:22:57 -0600 Subject: [PATCH 144/379] Add .type() accessor to CoxeterType --- src/sage/combinat/root_system/coxeter_type.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index 7f6b48482f9..3ef254f28e5 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -513,6 +513,18 @@ def cartan_type(self): """ return self._cartan_type + def type(self): + """ + Return the type of ``self``. + + EXAMPLES:: + + sage: C = CoxeterType(['A', 4]) + sage: C.type() + 'A' + """ + return self._cartan_type.type() + def rank(self): """ Return the rank of ``self``. From a3d02fd38daf4e74991eb1beb1ede3c73291d45e Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 14:28:25 -0600 Subject: [PATCH 145/379] Implement unique representation and redesign initialization a bit. Core data for uniq. rep is now a CoxeterGroup; add accessor and change _repr_ a bit to reflect this. --- .../combinat/fully_commutative_elements.py | 140 +++++++++++------- 1 file changed, 87 insertions(+), 53 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 6eefc5c329d..e5195b36655 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -27,10 +27,13 @@ from sage.structure.parent import Parent from sage.structure.list_clone import NormalizedClonableList from sage.categories.enumerated_sets import EnumeratedSets +from sage.structure.unique_representation import UniqueRepresentation from .root_system.coxeter_matrix import CoxeterMatrix from .root_system.cartan_type import CartanType from collections import deque from sage.combinat.posets.posets import Poset +from sage.categories.coxeter_groups import CoxeterGroups +from sage.combinat.root_system.coxeter_group import CoxeterGroup import itertools @@ -756,7 +759,7 @@ def star_operation(self, J, direction, side='left'): return self.parent().element_class(self.parent(), combined_data, check=False) -class FullyCommutativeElements(Parent): +class FullyCommutativeElements(Parent, UniqueRepresentation): r""" Class for the set of fully commutative (FC) elements of a Coxeter system. @@ -796,7 +799,10 @@ class FullyCommutativeElements(Parent): Create the enumerate set of fully commutative elements in `B_3`:: sage: FC = FullyCommutativeElements(['B', 3]); FC - Fully commutative elements in Coxeter system with Cartan type ['B', 3] + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: + [1 3 2] + [3 1 4] + [2 4 1] sage: FC.coxeter_matrix() [1 3 2] [3 1 4] @@ -870,51 +876,72 @@ class FullyCommutativeElements(Parent): sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] """ - - def __init__(self, data): + @staticmethod + def __classcall_private__(cls, data): r""" EXAMPLES:: - sage: FullyCommutativeElements(['B', 3]) - Fully commutative elements in Coxeter system with Cartan type ['B', 3] - sage: FullyCommutativeElements(CartanType(['B', 3])) - Fully commutative elements in Coxeter system with Cartan type ['B', 3] + sage: x1 = FullyCommutativeElements(CoxeterGroup(['B', 3])); x1 + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: + [1 3 2] + [3 1 4] + [2 4 1] + sage: x2 = FullyCommutativeElements(['B', 3]); x2 + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: + [1 3 2] + [3 1 4] + [2 4 1] + sage: x1 is x2 + True sage: FullyCommutativeElements(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})) - Fully commutative elements in Coxeter system with Cartan type ['B', 3] relabelled by {1: 3, 2: 2, 3: 1} + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: + [1 4 2] + [4 1 3] + [2 3 1] + sage: m = CoxeterMatrix([(1, 5, 2, 2, 2), (5, 1, 3, 2, 2), (2, 3, 1, 3, 2), (2, 2, 3, 1, 3), (2, 2, 2, 3, 1)]); FullyCommutativeElements(m) + Fully commutative elements of Coxeter group over Universal Cyclotomic Field with Coxeter matrix: + [1 5 2 2 2] + [5 1 3 2 2] + [2 3 1 3 2] + [2 2 3 1 3] + [2 2 2 3 1] """ - if isinstance(data, CoxeterMatrix): - self._matrix = data + if data in CoxeterGroups(): + group = data else: - try: - t = CartanType(data) - except (TypeError, ValueError): - raise ValueError( - 'input must be a CoxeterMatrix or data describing a Cartan type') - self._matrix = t.coxeter_matrix() + group = CoxeterGroup(data) + return super(cls, FullyCommutativeElements).__classcall__(cls, group) - self._index_set = sorted(self._matrix.index_set()) + def __init__(self, coxeter_group): + r""" + EXAMPLES:: + + sage: FullyCommutativeElements(CoxeterGroup(['H', 4])) + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: + [1 3 2 2] + [3 1 3 2] + [2 3 1 5] + [2 2 5 1] + """ + self._coxeter_group = coxeter_group # Start with the category of enumerated sets and refine it to finite or # infinite enumerated sets for Coxeter groups of Cartan types. category = EnumeratedSets() - # Finite groups will be FC-finite. - if self._matrix.is_finite(): - category = category.Finite() - else: - try: - cartan_type = self._matrix.coxeter_type().cartan_type() - family, rank, affine = cartan_type.type(), cartan_type.rank(), cartan_type.is_affine() - # The only groups of Cartan types that are infinite but - # FC-finite are affine `F_4` and affine `E_8`, which appear as - # `F_5` and `E_9` in [Ste1996]_. - if affine: - category = category.Finite() if (family == 'F' and rank == 4) or (family == 'E' and rank == 8) else category.Infinite() - else: - category = category.Infinite() - except AttributeError: - # no refinement for groups not corresponding to a Cartan type: - pass + ctype = self._coxeter_group.coxeter_type() + try: + family, rank = ctype.type(), ctype.rank() + # The only groups of Cartan types that are infinite but + # FC-finite are affine `F_4` and affine `E_8`, which appear as + # `F_5` and `E_9` in [Ste1996]_. + if not ctype.is_affine() or (family == 'F' and rank == 5) or (family == 'E' and rank == 8): + category = category.Finite() + else: + category = category.Infinite() + except AttributeError: + # ctype may just be a CoxeterMatrix, and we cannot identify it as a Cartan type. + pass Parent.__init__(self, category=category) @@ -923,21 +950,13 @@ def _repr_(self): EXAMPLES:: sage: FullyCommutativeElements(['H', 4]) - Fully commutative elements in Coxeter system with Cartan type ['H', 4] - sage: m = CoxeterMatrix([(1, 5, 2, 2, 2), (5, 1, 3, 2, 2), (2, 3, 1, 3, 2), (2, 2, 3, 1, 3), (2, 2, 2, 3, 1)]) - sage: FullyCommutativeElements(m) - Fully commutative elements in Coxeter system with Coxeter matrix - [1 5 2 2 2] - [5 1 3 2 2] - [2 3 1 3 2] - [2 2 3 1 3] - [2 2 2 3 1] + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: + [1 3 2 2] + [3 1 3 2] + [2 3 1 5] + [2 2 5 1] """ - try: - ctype = self.coxeter_matrix().coxeter_type().cartan_type() - return 'Fully commutative elements in Coxeter system with Cartan type {}'.format(str(ctype)) - except AttributeError: - return 'Fully commutative elements in Coxeter system with Coxeter matrix\n{}'.format(str(self.coxeter_matrix())) + return 'Fully commutative elements of {}'.format(str(self.coxeter_group())) def _element_constructor_(self, lst): r""" @@ -951,6 +970,21 @@ def _element_constructor_(self, lst): Element = FullyCommutativeElement + def coxeter_group(self): + r""" + Obtain the Coxeter group associated with ``self``. + + EXAMPLES:: + + sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3.coxeter_group() + Finite Coxeter group over Integer Ring with Coxeter matrix: + [1 3 2] + [3 1 3] + [2 3 1] + """ + return self._coxeter_group + def coxeter_matrix(self): r""" Obtain the Coxeter matrix of the associated Coxter system. @@ -972,7 +1006,7 @@ def coxeter_matrix(self): [2 2 3 1 4] [2 2 2 4 1] """ - return self._matrix + return self._coxeter_group.coxeter_matrix() def index_set(self): r""" @@ -985,12 +1019,12 @@ def index_set(self): sage: FCA3 = FullyCommutativeElements(['A', 3]) sage: FCA3.index_set() - [1, 2, 3] + (1, 2, 3) sage: FCB5 = FullyCommutativeElements(['B', 5]) sage: FCB5.index_set() - [1, 2, 3, 4, 5] + (1, 2, 3, 4, 5) """ - return self._index_set + return self._coxeter_group.index_set() def __iter__(self): r""" From 403fca9e114f16274a60f15eed3de51becbd5476 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 14:31:54 -0600 Subject: [PATCH 146/379] Remove FullyCommutativeElements from global namespace and expose through CoxeterGroup. --- src/sage/categories/coxeter_groups.py | 18 ++++++++++++++++++ src/sage/combinat/all.py | 2 -- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 9dbbc7b9467..bbd7a5b0557 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -606,6 +606,24 @@ def grassmannian_elements(self, side="right"): return self.weak_order_ideal(attrcall("is_grassmannian", side=side), side=order_side) + def fully_commutative_elements(self): + r""" + Return the combinatorial class of fully commutative elements in this + Coxeter group. See + :class:`~sage.combinat.fully_commutative_elements.FullyCommutativeElements` + for details. + + EXAMPLES:: + + sage: CoxeterGroup(['A', 3]).fully_commutative_elements() + Fully commutative elements of Finite Coxeter group over Integer Ring with Coxeter matrix: + [1 3 2] + [3 1 3] + [2 3 1] + """ + from sage.combinat.fully_commutative_elements import FullyCommutativeElements + return FullyCommutativeElements(self) + def _test_reduced_word(self, **options): """ Run sanity checks on :meth:`CoxeterGroups.ElementMethods.reduced_word` and diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 5d73a4072e4..40f1e598466 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -218,8 +218,6 @@ # sine-Gordon Y-systems lazy_import('sage.combinat.sine_gordon', 'SineGordonYsystem') -lazy_import('sage.combinat.fully_commutative_elements', 'FullyCommutativeElements') - # Fully Packed Loop lazy_import('sage.combinat.fully_packed_loop', ['FullyPackedLoop', 'FullyPackedLoops']) From dfae2719f9e432fecd4a3e1bcc48347e184c8f82 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 14:44:54 -0600 Subject: [PATCH 147/379] Fix tests to reflect new usage method. --- .../combinat/fully_commutative_elements.py | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index e5195b36655..c9d8952052e 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -58,9 +58,9 @@ def check(self): TESTS:: - sage: FullyCommutativeElements(['A', 3])([1, 2]) # indirect doctest + sage: CoxeterGroup(['A', 3]).fully_commutative_elements()([1, 2]) # indirect doctest [1, 2] - sage: FullyCommutativeElements(['A', 3])([1, 2, 1]) # indirect doctest + sage: CoxeterGroup(['A', 3]).fully_commutative_elements()([1, 2, 1]) # indirect doctest Traceback (most recent call last): ... ValueError: the input is not a reduced word of a fully commutative element @@ -75,7 +75,7 @@ def normalize(self): TESTS:: - sage: FullyCommutativeElements(['A', 3])([3, 1]) # indirect doctest + sage: CoxeterGroup(['A', 3]).fully_commutative_elements()([3, 1]) # indirect doctest [1, 3] """ return self.cartier_foata_form() @@ -100,7 +100,7 @@ def is_fully_commutative(self): TESTS:: - sage: FC = FullyCommutativeElements(['A', 3]) + sage: FC = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: x = FC([1, 2]); x.is_fully_commutative() True sage: x = FC.element_class(FC, [1, 2, 1], check=False); x.is_fully_commutative() @@ -157,7 +157,7 @@ def cartier_foata_form(self): The following reduced words express the same FC elements in `B_5`:: - sage: FC = FullyCommutativeElements(['B', 5]) + sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: FC([1, 4, 3, 5, 2, 4, 3]) # indirect doctest [1, 4, 3, 5, 2, 4, 3] sage: FC([4, 1, 3, 5, 2, 4, 3]) # indirect doctest @@ -220,7 +220,7 @@ def heap(self, **kargs): EXAMPLES:: - sage: FC = FullyCommutativeElements(['A', 5]) + sage: FC = CoxeterGroup(['A', 5]).fully_commutative_elements() sage: FC([1, 4, 3, 5, 2, 4]).heap().cover_relations() [[1, 2], [1, 3], [2, 5], [2, 4], [3, 5], [0, 4]] sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() @@ -264,7 +264,7 @@ def plot_heap(self): EXAMPLES:: - sage: FC = FullyCommutativeElements(['B', 5]) + sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: FC([3,2,4,3,1]).plot_heap() Graphics object consisting of 15 graphics primitives @@ -319,7 +319,7 @@ def n_value(self): EXAMPLES:: - sage: FC = FullyCommutativeElements(['A', 5]) + sage: FC = CoxeterGroup(['A', 5]).fully_commutative_elements() sage: FC([1,3]).n_value() 2 sage: FC([1,2,3]).n_value() @@ -374,7 +374,7 @@ def find_descent(self, s, side='left'): EXAMPLES:: - sage: FC = FullyCommutativeElements(['B', 5]) + sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: w = FC([1, 4, 3, 5, 2, 4, 3]) sage: w.find_descent(1) 0 @@ -407,7 +407,7 @@ def has_descent(self, s, side='left'): EXAMPLES:: - sage: FC = FullyCommutativeElements(['B', 5]) + sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: w = FC([1, 4, 3, 5, 2, 4, 3]) sage: w.has_descent(1) True @@ -440,13 +440,13 @@ def descents(self, side='left'): EXAMPLES:: - sage: FC = FullyCommutativeElements(['B', 5]) + sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: w = FC([1, 4, 3, 5, 2, 4, 3]) sage: sorted(w.descents()) [1, 4] sage: w.descents(side='right') {3} - sage: FC = FullyCommutativeElements(['A', 5]) + sage: FC = CoxeterGroup(['A', 5]).fully_commutative_elements() sage: sorted(FC([1, 4, 3, 5, 2, 4, 3]).descents()) [1, 4] @@ -486,7 +486,7 @@ def coset_decomposition(self, J, side='left'): EXAMPLES:: - sage: FC = FullyCommutativeElements(['B', 6]) + sage: FC = CoxeterGroup(['B', 6]).fully_commutative_elements() sage: w = FC([1, 6, 2, 5, 4, 6, 5]) sage: w.coset_decomposition({1}) ([1], [6, 2, 5, 4, 6, 5]) @@ -549,7 +549,7 @@ def still_reduced_fc_after_prepending(self, s): Consider the FC element `w = 12` in the group `B_3`:: - sage: FCB3 = FullyCommutativeElements(['B', 3]) + sage: FCB3 = CoxeterGroup(['B', 3]).fully_commutative_elements() sage: FCB3.coxeter_matrix() [1 3 2] [3 1 4] @@ -582,7 +582,7 @@ def still_reduced_fc_after_prepending(self, s): sage: u.still_reduced_fc_after_prepending(3) False - sage: FCA5 = FullyCommutativeElements(['A',5]) + sage: FCA5 = CoxeterGroup(['A', 5]).fully_commutative_elements() sage: w = FCA5([2,4,1,3,2,5]) sage: w.still_reduced_fc_after_prepending(5) False @@ -696,7 +696,7 @@ def star_operation(self, J, direction, side='left'): We will compute all star operations on the following FC element in type `B_6` relative to `J = \{5, 6\}`:: - sage: FC = FullyCommutativeElements(['B', 6]) + sage: FC = CoxeterGroup(['B', 6]).fully_commutative_elements() sage: w = FC([1, 6, 2, 5, 4, 6, 5]) Whether and how a left star operations can be applied depend on the @@ -798,7 +798,7 @@ class FullyCommutativeElements(Parent, UniqueRepresentation): Create the enumerate set of fully commutative elements in `B_3`:: - sage: FC = FullyCommutativeElements(['B', 3]); FC + sage: FC = CoxeterGroup(['B', 3]).fully_commutative_elements(); FC Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: [1 3 2] [3 1 4] @@ -842,7 +842,7 @@ class FullyCommutativeElements(Parent, UniqueRepresentation): Enumerate the FC elements in `A_3`:: - sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: FCA3.category() Category of finite enumerated sets sage: FCA3.list() @@ -863,14 +863,14 @@ class FullyCommutativeElements(Parent, UniqueRepresentation): Count the FC elements in `B_8`:: - sage: FCB8 = FullyCommutativeElements(['B', 8]) + sage: FCB8 = CoxeterGroup(['B', 8]).fully_commutative_elements() sage: len(FCB8) # long time (7 seconds) 14299 Iterate through the FC elements of length up to 2 in the non-FC-finite group affine `A_2`:: - sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) + sage: FCAffineA2 = CoxeterGroup(['A', 2, 1]).fully_commutative_elements() sage: FCAffineA2.category() Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(2)) @@ -881,12 +881,13 @@ def __classcall_private__(cls, data): r""" EXAMPLES:: + sage: from sage.combinat.fully_commutative_elements import FullyCommutativeElements sage: x1 = FullyCommutativeElements(CoxeterGroup(['B', 3])); x1 Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: [1 3 2] [3 1 4] [2 4 1] - sage: x2 = FullyCommutativeElements(['B', 3]); x2 + sage: x2 = CoxeterGroup(['B', 3]).fully_commutative_elements(); x2 Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: [1 3 2] [3 1 4] @@ -916,6 +917,7 @@ def __init__(self, coxeter_group): r""" EXAMPLES:: + sage: from sage.combinat.fully_commutative_elements import FullyCommutativeElements sage: FullyCommutativeElements(CoxeterGroup(['H', 4])) Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: [1 3 2 2] @@ -949,7 +951,7 @@ def _repr_(self): r""" EXAMPLES:: - sage: FullyCommutativeElements(['H', 4]) + sage: CoxeterGroup(['H', 4]).fully_commutative_elements() Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: [1 3 2 2] [3 1 3 2] @@ -962,7 +964,7 @@ def _element_constructor_(self, lst): r""" TESTS:: - sage: FC = FullyCommutativeElements(['A', 3]) + sage: FC = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: FC([1, 2]) # indirect doctest [1, 2] """ @@ -976,7 +978,7 @@ def coxeter_group(self): EXAMPLES:: - sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: FCA3.coxeter_group() Finite Coxeter group over Integer Ring with Coxeter matrix: [1 3 2] @@ -993,12 +995,12 @@ def coxeter_matrix(self): EXAMPLES:: - sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: FCA3.coxeter_matrix() [1 3 2] [3 1 3] [2 3 1] - sage: FCB5 = FullyCommutativeElements(['B', 5]) + sage: FCB5 = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: FCB5.coxeter_matrix() [1 3 2 2 2] [3 1 3 2 2] @@ -1017,10 +1019,10 @@ def index_set(self): EXAMPLES:: - sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: FCA3.index_set() (1, 2, 3) - sage: FCB5 = FullyCommutativeElements(['B', 5]) + sage: FCB5 = CoxeterGroup(['B', 5]).fully_commutative_elements() sage: FCB5.index_set() (1, 2, 3, 4, 5) """ @@ -1034,7 +1036,7 @@ def __iter__(self): TESTS:: - sage: FC = FullyCommutativeElements(['A', 10]) + sage: FC = CoxeterGroup(['A', 10]).fully_commutative_elements() sage: next(iter(FC)) # indirect doctest [] """ @@ -1077,7 +1079,7 @@ def iterate_to_length(self, length): The following example produces all FC elements of length up to 2 in the group `A_3`:: - sage: FCA3 = FullyCommutativeElements(['A', 3]) + sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() sage: list(FCA3.iterate_to_length(2)) [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3]] @@ -1096,7 +1098,7 @@ def iterate_to_length(self, length): The following example produces all FC elements of length up to 4 in the affine Weyl group `\tilde A_2`:: - sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1]) + sage: FCAffineA2 = CoxeterGroup(['A', 2, 1]).fully_commutative_elements() sage: FCAffineA2.category() Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(4)) From b1e3db538a332c1b8470d2176629e663b5e05ebb Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 14:50:32 -0600 Subject: [PATCH 148/379] Add .group_element() method on fully commutative elements. --- .../combinat/fully_commutative_elements.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index c9d8952052e..7dd6d8453c5 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -80,6 +80,25 @@ def normalize(self): """ return self.cartier_foata_form() + def group_element(self): + r""" + Get the actual element of the Coxeter group associated with + ``self.parent()`` corresponding to ``self``. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A', 3]) + sage: FC = W.fully_commutative_elements() + sage: x = FC([1, 2]) + sage: x.group_element() + [ 0 -1 1] + [ 1 -1 1] + [ 0 0 1] + sage: x.group_element() in W + True + """ + return self.parent().coxeter_group().from_reduced_word(self) + ########################################################################### # Characterization and representation of FC elements # ########################################################################### From 552fa0c11b894520a01fcd7f24afacda565ecbcf Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 15:34:35 -0600 Subject: [PATCH 149/379] Remove superfluous accessors --- .../combinat/fully_commutative_elements.py | 67 +++---------------- 1 file changed, 9 insertions(+), 58 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 7dd6d8453c5..70662b87a4e 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -125,7 +125,7 @@ def is_fully_commutative(self): sage: x = FC.element_class(FC, [1, 2, 1], check=False); x.is_fully_commutative() False """ - matrix = self.parent().coxeter_matrix() + matrix = self.parent().coxeter_group().coxeter_matrix() w = tuple(self) # The following function detects 'braid' words. @@ -245,7 +245,7 @@ def heap(self, **kargs): sage: FC([1, 4, 3, 5, 4, 2]).heap(one_index=True).cover_relations() [[2, 3], [2, 4], [3, 6], [3, 5], [4, 6], [1, 5]] """ - m = self.parent().coxeter_matrix() + m = self.parent().coxeter_group().coxeter_matrix() one_index = kargs.get('one_index', False) display_labeling = kargs.get('display_labeling', False) @@ -295,8 +295,8 @@ def plot_heap(self): """ import sage.plot.all as plot - m = self.parent().coxeter_matrix() - letters = self.parent().index_set() + m = self.parent().coxeter_group().coxeter_matrix() + letters = self.parent().coxeter_group().index_set() graphics = [] h = self.heap() @@ -406,7 +406,7 @@ def find_descent(self, s, side='left'): sage: w.find_descent(3) """ - m = self.parent().coxeter_matrix() + m = self.parent().coxeter_group().coxeter_matrix() view = list(self) if side == 'left' else self[::-1] for (i, t) in enumerate(view): if t == s and not any(m[x, t] > 2 for x in view[:i]): @@ -472,7 +472,7 @@ def descents(self, side='left'): .. SEEALSO:: :func:`find_descent` """ view = list(self) if side == 'left' else self[::-1] - m = self.parent().coxeter_matrix() + m = self.parent().coxeter_group().coxeter_matrix() out = set() for (i, t) in enumerate(view): if not any(m[x, t] > 2 for x in view[:i]): @@ -569,10 +569,6 @@ def still_reduced_fc_after_prepending(self, s): Consider the FC element `w = 12` in the group `B_3`:: sage: FCB3 = CoxeterGroup(['B', 3]).fully_commutative_elements() - sage: FCB3.coxeter_matrix() - [1 3 2] - [3 1 4] - [2 4 1] sage: w = FCB3([1,2]) When `s=1`, `sw` is 112, which is not reduced:: @@ -644,7 +640,7 @@ def still_reduced_fc_after_prepending(self, s): See Lemma 4.1 of [Ste1996]_. """ - m = self.parent().coxeter_matrix() + m = self.parent().coxeter_group().coxeter_matrix() if self.has_descent(s): return False @@ -747,7 +743,7 @@ def star_operation(self, J, direction, side='left'): """ assert len(J) == 2, 'J needs to contain a pair of generators.' s, t = J - mst = self.parent().coxeter_matrix()[s, t] + mst = self.parent().coxeter_group().coxeter_matrix()[s, t] # Perform the coset decomposition on the specified side: if side == 'left': @@ -822,10 +818,6 @@ class FullyCommutativeElements(Parent, UniqueRepresentation): [1 3 2] [3 1 4] [2 4 1] - sage: FC.coxeter_matrix() - [1 3 2] - [3 1 4] - [2 4 1] Construct elements:: @@ -1006,47 +998,6 @@ def coxeter_group(self): """ return self._coxeter_group - def coxeter_matrix(self): - r""" - Obtain the Coxeter matrix of the associated Coxter system. - - OUTPUT: CoxeterMatrix - - EXAMPLES:: - - sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() - sage: FCA3.coxeter_matrix() - [1 3 2] - [3 1 3] - [2 3 1] - sage: FCB5 = CoxeterGroup(['B', 5]).fully_commutative_elements() - sage: FCB5.coxeter_matrix() - [1 3 2 2 2] - [3 1 3 2 2] - [2 3 1 3 2] - [2 2 3 1 4] - [2 2 2 4 1] - """ - return self._coxeter_group.coxeter_matrix() - - def index_set(self): - r""" - Obtain the set of the generators/simple reflections of the associated - Coxeter system. - - OUTPUT: iterable of integers - - EXAMPLES:: - - sage: FCA3 = CoxeterGroup(['A', 3]).fully_commutative_elements() - sage: FCA3.index_set() - (1, 2, 3) - sage: FCB5 = CoxeterGroup(['B', 5]).fully_commutative_elements() - sage: FCB5.index_set() - (1, 2, 3, 4, 5) - """ - return self._coxeter_group.index_set() - def __iter__(self): r""" Enumerate the elements of this set by length, starting with the empty @@ -1060,7 +1011,7 @@ def __iter__(self): [] """ empty_word = self.element_class(self, [], check=False) - letters = self.index_set() + letters = self.coxeter_group().index_set() # To make the iterator deterministic, use a dictionary rather than a # set, for the keys are then ordered by default by Python 3.7+: From 1319113ccacabe2e76320877b0c3e64d6d123f95 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 15:35:05 -0600 Subject: [PATCH 150/379] Typo in rank for FC-finite classification. --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 70662b87a4e..967a159c77f 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -948,7 +948,7 @@ def __init__(self, coxeter_group): # The only groups of Cartan types that are infinite but # FC-finite are affine `F_4` and affine `E_8`, which appear as # `F_5` and `E_9` in [Ste1996]_. - if not ctype.is_affine() or (family == 'F' and rank == 5) or (family == 'E' and rank == 8): + if not ctype.is_affine() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9): category = category.Finite() else: category = category.Infinite() From d4f53de6ef04cdc572aacad4e8dcc8bcb4bedca9 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 15:53:52 -0600 Subject: [PATCH 151/379] Improve comments on category refinement. --- src/sage/combinat/fully_commutative_elements.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 967a159c77f..1be5ff01661 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -944,16 +944,19 @@ def __init__(self, coxeter_group): ctype = self._coxeter_group.coxeter_type() try: + # A 'letter' family is defined if and only if the Coxeter group is finite or affine: family, rank = ctype.type(), ctype.rank() - # The only groups of Cartan types that are infinite but - # FC-finite are affine `F_4` and affine `E_8`, which appear as - # `F_5` and `E_9` in [Ste1996]_. + # All finite Coxeter groups are certainly FC-finite. Of the affine Coxeter groups only + # the groups affine `F_4` and affine `E_8` are FC-finite; they have rank 5 and rank 9 + # and correspond to the groups `F_5` and `E_9` in [Ste1996]_, respectively: if not ctype.is_affine() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9): category = category.Finite() else: category = category.Infinite() except AttributeError: - # ctype may just be a CoxeterMatrix, and we cannot identify it as a Cartan type. + # No refinement is specified for Coxeter groups that are not finite or affine + # (Note that this includes groups of the form E_n (n>9), F_n (n>5) and H_n (n>4) + # from [Ste1996]_ which are known to be FC-finite) pass Parent.__init__(self, category=category) From 5cbdca30bbbc372c29b5f310f95acb15dac4d17a Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Thu, 30 Jul 2020 15:57:32 -0600 Subject: [PATCH 152/379] Improve unique representation test. --- src/sage/combinat/fully_commutative_elements.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 1be5ff01661..86e04def474 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -898,12 +898,17 @@ def __classcall_private__(cls, data): [1 3 2] [3 1 4] [2 4 1] - sage: x2 = CoxeterGroup(['B', 3]).fully_commutative_elements(); x2 + sage: x2 = FullyCommutativeElements(['B', 3]); x2 Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: [1 3 2] [3 1 4] [2 4 1] - sage: x1 is x2 + sage: x3 = FullyCommutativeElements(CoxeterMatrix([[1, 3, 2], [3, 1, 4], [2, 4, 1]])); x3 + Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: + [1 3 2] + [3 1 4] + [2 4 1] + sage: x1 is x2 is x3 True sage: FullyCommutativeElements(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})) Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: From f640e21f46cf3ab10880fe444a725e409ee94aea Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 31 Jul 2020 15:22:49 -0400 Subject: [PATCH 153/379] 29844: fixed minor spacing --- src/sage/schemes/berkovich/berkovich_cp_element.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index a9778017857..36505605afb 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -364,11 +364,10 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= raise ValueError('type II and III points can not be centered at infinity') if power != None: if error_check: - if not(power.parent() is QQ): - try: - power = QQ(power) - except TypeError: - raise TypeError("power must convert to rationals") + try: + power = QQ(power) + except TypeError: + raise TypeError("power must convert to rationals") if radius != None: if radius != RR(self._p**power): raise ValueError("conflicting inputs for power and radius") @@ -569,7 +568,7 @@ def radius(self): sage: Q1.radius() 0.400000000000000 - :: + :: sage: d = B([2, 2, 2], [1.761, 1.123, 1.112]) sage: d.radius() @@ -604,7 +603,7 @@ def diameter(self, basepoint=Infinity): sage: Q1.diameter() 0 - :: + :: sage: Q2 = B(1/2, 9) sage: Q2.diameter() From c83784ac1ee2b15fe5a2471da389b069d63aa318 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Fri, 31 Jul 2020 16:00:46 -0400 Subject: [PATCH 154/379] 29844: polynomial ring can be passed to projective Berkovich --- src/sage/schemes/berkovich/all.py | 2 -- src/sage/schemes/berkovich/berkovich_space.py | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/berkovich/all.py b/src/sage/schemes/berkovich/all.py index 0c26ff503ed..cc7af01a057 100644 --- a/src/sage/schemes/berkovich/all.py +++ b/src/sage/schemes/berkovich/all.py @@ -1,7 +1,5 @@ """nodoctest all.py -- export of Berkovich spaces to all of Sage """ -from __future__ import absolute_import from .berkovich_space import Berkovich_Cp_Affine, Berkovich_Cp_Projective - diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index fabcfc46570..4da06fb6ef7 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -538,7 +538,10 @@ def __init__(self, base, ideal=None): if base in NumberFields() or is_pAdicField(base): base = ProjectiveSpace(base, 1) if not is_ProjectiveSpace(base): - raise ValueError("base of projective Berkovich space must be projective space") + try: + base = ProjectiveSpace(base) + except: + raise ValueError("base of projective Berkovich space must be projective space") if not (is_pAdicField(base.base_ring())): if base.base_ring() not in NumberFields(): raise ValueError("base of projective Berkovich space must be " + \ From 8a18f5221041e07feba4dea80dd7b8891f5432c7 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 2 Aug 2020 18:07:33 -0400 Subject: [PATCH 155/379] Added the computation of ribbon and anchor of mobile posets --- src/sage/combinat/posets/mobile.py | 197 +++++++++++++++++++---------- src/sage/graphs/generic_graph.py | 4 + 2 files changed, 136 insertions(+), 65 deletions(-) diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index 4f3b0f3a205..4844c9b2179 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -10,83 +10,144 @@ from sage.combinat.posets.posets import Poset, FinitePoset from sage.combinat.posets.d_complete import DCompletePoset +from sage.misc.lazy_attribute import lazy_attribute + #from .linear_extensions import LinearExtensionsOfForest class MobilePoset(FinitePoset): r""" Mobile posets are an extension of d-complete posets which permit a determinant formula for counting linear extensions. - """ #_lin_ext_type = LinearExtensionsOfForest _desc = 'Finite mobile poset' - def _mobile_structure(self): + + def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None): + FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, category=category, facade=facade, key=key) + if ribbon and self._test_valid_ribbon(ribbon): + self._ribbon = ribbon + + def _test_valid_ribbon(self, ribbon): + r""" + Returns True if a ribbon has at most one anchor and that no vertex has two or more anchors + + INPUT: + + - ``ribbon`` -- a list of elements that form a ribbon in your poset + """ + ribbon = list(map(lambda x: self._element_to_vertex(x), ribbon)) + H = self._hasse_diagram + R = H.subgraph(ribbon) + num_anchors = 0 + for r in ribbon: + anchor_neighbors = set(H.neighbors_out(r)).difference(set(R.neighbors_out(r))) + if len(anchor_neighbors) == 1: + num_anchors += 1 + elif len(anchor_neighbors) > 1: + return False + + return num_anchors <= 1 + + + @lazy_attribute + def _anchor(self): + r""" + The anchor of the mobile poset. + + TESTS:: + + sage: from sage.combinat.posets.mobile import MobilePoset + sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) + sage: M._anchor + (4, 3) + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: M2._anchor + (4, 3) + sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) + sage: M3._anchor is None + True + """ + ribbon = list(map(lambda x: self._element_to_vertex(x), self._ribbon)) + H = self._hasse_diagram + R = H.subgraph(ribbon) + + anchor = None + + # Find the anchor vertex, if it exists, and return the edge + for r in ribbon: + anchor_neighbors = set(H.neighbors_out(r)).difference(set(R.neighbors_out(r))) + if len(anchor_neighbors) == 1: + anchor = (r, anchor_neighbors.pop()) + break + return (self._vertex_to_element(anchor[0]), self._vertex_to_element(anchor[1])) if not anchor is None else None + + + + @lazy_attribute + def _ribbon(self): + r""" + The ribbon of the mobile poset. + + TESTS:: + + sage: from sage.combinat.posets.mobile import MobilePoset + sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) + sage: sorted(M._ribbon) + [4, 5, 6, 7, 8] + sage: M._test_valid_ribbon(M._ribbon) + True + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: sorted(M2._ribbon) + [4, 7, 8] + sage: M2._test_valid_ribbon(M2._ribbon) + True + sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) + sage: sorted(M3._ribbon) + [1, 2, 3, 4] + sage: M3._test_valid_ribbon(M3._ribbon) + True + """ + return list(map(lambda x: self._vertex_to_element(x), self._compute_ribbon())) + + def _compute_ribbon(self): + r""" + The helper function of _ribbon for computing the ribbon. + """ H = self._hasse_diagram H_un = H.to_undirected() max_elmts = H.sinks() - # Compute plant, ribbon + # Compute anchor, ribbon ribbon = [] # In order list of elements on zigzag - plant = (0, 0) # The cut edge separating the plant from the zigzag - + if len(max_elmts) == 1: + return [max_elmts[0]] # Compute max element tree by merging shortest paths - start = max_elmts[0] zigzag_elmts = set() for m in max_elmts[1:]: - sp = G.shortest_path(start, m) - zigzag_elmts.add(sp) + sp = H_un.shortest_path(start, m) + zigzag_elmts.update(sp) max_elmt_graph = H.subgraph(zigzag_elmts) G = max_elmt_graph.to_undirected() if G.is_path(): - # Check if there is a plant by seeing if there is more than one acyclic path to the next max + # Check if there is a anchor by seeing if there is more than one acyclic path to the next max ends = max_elmt_graph.vertices_with_degree(1) - # Form ribbon ribbon = G.shortest_path(ends[0], ends[1]) - - for end in ends: - path = [] - nextElmt = end - # Get next maximal element in zigzag - while True: - path.append(nextElmt) - nbrs = G.neighbors(nextElmt) - nextElmt = nbrs[0] if nbrs[0] != nextElmt else nbrs[1] - if nextElmt in max_elmts: - break - - # Find spot where we enter zigzag - foundPlant = False - - for i, v in enumerate(path): - if len(H_un.all_paths(v, nextElmt)) > 1: - foundPlant = True - continue - elif i == 0: - break - if foundPlant: - plant = (v, path[i-1]) - # Shorten zigzag - - endIndex = ribbon.index(end) - plantIndex = ribbon.index(v) - - if plantIndex < endIndex: - ribbon = ribbon[:plantIndex] - else: - ribbon = ribbon[plantIndex:] + for end_count, end in enumerate(ends): + if not (H_un.is_cut_vertex(end) or H_un.degree(end) == 1): + traverse_ribbon = ribbon if end_count == 0 else ribbon[::-1] + for ind, p in enumerate(traverse_ribbon): + if H_un.is_cut_edge(p, traverse_ribbon[ind+1]): + return G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1]) + return ribbon - break - - if foundPlant: - break else: # First check path counts between ends and deg3 vertex @@ -96,34 +157,40 @@ def _mobile_structure(self): ends = max_elmt_graph.vertices_with_degree(1) deg3 = max_elmt_graph.vertices_with_degree(3)[0] - plantedEnd = None + anchoredEnd = None for end in ends: - if H_un.all_paths(end, deg3) > 1: - plantedEnd = end + if not (H_un.is_cut_vertex(end) or H_un.degree(end) == 1): + anchoredEnd = end break - if not plantedEnd is None: - path = H.shortest_path(deg3, plantedEnd) - plant = (path[0], path[1]) + if not anchoredEnd is None: + path = H.shortest_path(deg3, anchoredEnd) + ends.remove(anchoredEnd) - ends.remove(plantedEnd) - ribbon = max_elmt_graph.shortest_path(end[0], end[1]) + return G.shortest_path(ends[0], ends[1]) else: - possible_plants = ends[:] + possible_anchors = ends[:] for end in ends: path = G.shortest_path(end, deg3) - if not reduce(lambda x,y: x^y, map(lambda z: z in max_elmts, path)): - possible_plants.remove(end) + if not sum(map(lambda z: z in max_elmts, path)) == 1: + possible_anchors.remove(end) - plantedEnd = possible_plants[0] - ends.remove(plantedEnd) - ribbon = G.shortest_path(ends[0], ends[1]) - - plant = (deg3, G.shortest_path(deg3, plantedEnd)[1]) - - self._ribbon = ribbon - self._plant = plant + anchoredEnd = possible_anchors[0] + ends.remove(anchoredEnd) + return G.shortest_path(ends[0], ends[1]) + + def get_ribbon(self): + r""" + Returns the ribbon of the mobile poset + """ + return self._ribbon + + def get_anchor(self): + r""" + Returns the anchor of the mobile poset + """ + return self._anchor diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index cdcb4c9519e..8963bb2f3ba 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12088,6 +12088,10 @@ def vertices_with_degree(self, degree): r""" Return a list of vertices with the indicated degree. + INPUT: + + - ``degree`` -- a degree to match with each vertex in the graph + EXAMPLES: sage: G = graphs.CompleteGraph(5) From 9d3e29ad1d5799df8083818ea2c2f0226e1d8fa6 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 3 Aug 2020 01:08:28 -0400 Subject: [PATCH 156/379] added slant sums in prep for linear extensions --- src/sage/combinat/posets/posets.py | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 9e04daddfdd..45e3c82787d 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -2373,6 +2373,55 @@ def is_d_complete(self): break return True + + @staticmethod + def slant_sum(P1, P2, P1_element, P2_element): + r""" + Return the slant sum poset of posets P1 and P2 by connecting them with a + cover relation (P1_element, P2_element). Element names of P1 and P2 must be distinct. + + INPUT: + + - ``P1`` -- The first poset + - ``P2`` -- The second poset + - ``P1_element`` -- The element of P1 that is the bottom of the new cover relation + - ``P2_element`` -- The element of P2 that is the top of the new cover relation + + EXAMPLES:: + + sage: from sage.combinat.posets.posets import FinitePoset + sage: R = Posets.RibbonPoset(5, [1,2]) + sage: H = Poset([[5, 6, 7], [(5, 6), (6,7)]]) + sage: SS = FinitePoset.slant_sum(H, R, 7, 3) + sage: SS.cover_relations() + [[5, 6], [6, 7], [7, 3], [3, 4], [3, 2], [2, 1], [0, 1]] + """ + elements = P1._elements + P2._elements + cover_relations = P1.cover_relations() + P2.cover_relations() + cover_relations.append((P1_element, P2_element)) + return Poset([elements, cover_relations]) + + @staticmethod + def multislant_sum(psets, relations): + r""" + Return the union of posets provided in ``psets``, along with the additional cover relations + specified in ``relations`` + + INPUT: + + - ``psets`` -- A list of finite posets + - ``relations`` -- A list of new cover relations + """ + elements = [] + cover_relations = [] + for p in psets: + elements.extend(p._elements) + cover_relations.extend(p.cover_relations()) + + for r in relations: + cover_relations.append(r) + + return Poset([elements, cover_relations]) def intervals_poset(self): r""" From 780b1d780a32a03a9eb3aefaa70b510b9bd57de3 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 3 Aug 2020 11:18:48 -0400 Subject: [PATCH 157/379] 29844: added affine space as option for affine berk space --- src/sage/schemes/berkovich/berkovich_space.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 4da06fb6ef7..26e30314080 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -34,6 +34,7 @@ from sage.schemes.berkovich.berkovich_cp_element import (Berkovich_Element_Cp_Affine, Berkovich_Element_Cp_Projective) from sage.structure.parent import Parent +from sage.schemes.affine.affine_space import is_AffineSpace from sage.schemes.projective.projective_space import is_ProjectiveSpace, ProjectiveSpace from sage.structure.unique_representation import UniqueRepresentation from sage.categories.number_fields import NumberFields @@ -348,6 +349,12 @@ class Berkovich_Cp_Affine(Berkovich_Cp): ... ValueError: could not convert c to Number Field in a with defining polynomial x^3 + 20 + + TESTS:: + + sage: A. = AffineSpace(Qp(3), 1) + sage: Berkovich_Cp_Affine(A) + Affine Berkovich line over Cp(3) of precision 20 """ Element = Berkovich_Element_Cp_Affine @@ -358,6 +365,8 @@ def __init__(self, base, ideal=None): base = Qp(base) #TODO chance to Qpbar else: raise ValueError("non-prime pased into Berkovich space") + if is_AffineSpace(base): + base = base.base_ring() if base in NumberFields(): if ideal == None: raise ValueError('passed a number field but not an ideal') From 2b5d6ede1267f6aa96577dbb4d62ecc9eb5150c2 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 3 Aug 2020 14:55:22 -0400 Subject: [PATCH 158/379] 29844: deleted trailing whitespace --- .../schemes/berkovich/berkovich_cp_element.py | 20 +++++++++---------- src/sage/schemes/berkovich/berkovich_space.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 36505605afb..aa5c44615ad 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -1049,8 +1049,8 @@ def _latex_(self): sage: B = Berkovich_Cp_Projective(3) sage: latex(B(2, 1)) - \text{type 2 Point of } \text{Projective Berkovich line over } - \Bold{C}_{3} \text{equivalent to the disk centered at + \text{type 2 Point of } \text{Projective Berkovich line over } + \Bold{C}_{3} \text{equivalent to the disk centered at (2 + O(3^20) : 1 + O(3^20)) of radius 1.00000000000000 in } \Bold{C}_3 """ from sage.misc.latex import latex @@ -1086,7 +1086,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): INPUT: - - ``center`` -- For type I, II, and III points, the center of the + - ``center`` -- For type I, II, and III points, the center of the corresponding disk in `\CC_p`. If the parent Berkovich space was created using a number field `K`, then ``center`` must be an element of `K`. Otherwise, ``center`` must be an element of a p-adic field. For type IV points, can be a list of centers used to approximate the point or a @@ -1145,7 +1145,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): from a list of centers and radii used to approximate the point:: sage: B([Qp(3)(2), Qp(3)(2), Qp(3)(2)], [1.761, 1.123, 1.112]) - Type IV point of precision 3, approximated by disks centered at + Type IV point of precision 3, approximated by disks centered at [2 + O(3^20), 2 + O(3^20)] ... with radii [1.76100000000000, 1.12300000000000] ... Type IV points can be constructed from univariate functions, with arbitrary precision:: @@ -1157,7 +1157,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: S = FractionField(S) sage: g = (y + 1)/y sage: d = B(f, g, prec=100); d - Type IV point of precision 100 with centers given by + Type IV point of precision 100 with centers given by ((t^2 + 2*t + 1) + O(3^20))*x and radii given by (y + 1.00000000000000)/y For increased performance, error_check can be set to ``False``. WARNING: with error check set @@ -1771,7 +1771,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): INPUT: - - ``center`` -- For type I, II, and III points, the center of the + - ``center`` -- For type I, II, and III points, the center of the corresponding disk in `P^1(\CC_p)`. If the parent Berkovich space was created using a number field `K`, then ``center`` can be an element of `P^1(K)`. Otherwise, ``center`` must be an element of a projective space of dimension 1 over a padic field. @@ -1830,11 +1830,11 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: L = [b, c, d] sage: R = [1.761, 1.123, 1.112] sage: Q5 = P(L, R); Q5 - Type IV point of precision 3, approximated by disks centered at + Type IV point of precision 3, approximated by disks centered at [(4 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + - 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20) : - 1 + O(5^20)), (3 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + - 5^10 + 3*5^11 + 5^12 + 3*5^13 + 5^14 + 3*5^15 + 5^16 + 3*5^17 + 5^18 + 3*5^19 + O(5^20) : + 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20) : + 1 + O(5^20)), (3 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + + 5^10 + 3*5^11 + 5^12 + 3*5^13 + 5^14 + 3*5^15 + 5^16 + 3*5^17 + 5^18 + 3*5^19 + O(5^20) : 1 + O(5^20))] ... with radii [1.76100000000000, 1.12300000000000] ... Type IV points can also be created from univariate functions. Since the centers of diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 26e30314080..0c19879e0f0 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -242,7 +242,7 @@ def __ne__(self,right): class Berkovich_Cp_Affine(Berkovich_Cp): r""" The Berkovich affine line over `\CC_p`. - + The Berkovich affine line is the set of seminorms on `\CC_p[x]`, with the weakest topology such that the map `| \cdot | \to |f|` is continuous for all `f \in \CC_p[x]`. From c5ced640041e36f8b7421d00f1c945841d392655 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 3 Aug 2020 15:58:21 -0400 Subject: [PATCH 159/379] 29844: added note in scheme overview --- src/sage/schemes/overview.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/schemes/overview.py b/src/sage/schemes/overview.py index 95d081247e0..352f46b0a97 100644 --- a/src/sage/schemes/overview.py +++ b/src/sage/schemes/overview.py @@ -138,6 +138,19 @@ In fact, we need a test ``R.ideal([2, 1 + t]) == R.ideal([1])`` in order to make this meaningful. +Berkovich Analytic Spaces +------------------------- + +- **Berkovich Analytic Space (not yet implemented)** + The construction of analytic spaces from schemes due to Berkovich. Any Berkovich + space should inherit from :class:`Berkovich` + +- **Berkovich Analytic Space over Cp** + A special case of the general Berkovich analytic space construction. + Affine Berkovich space over `\CC_p` is the set of seminorms on the polynomial + ring `\CC_p[x]`, while projective Berkovich space over `\CC_p` is the one-point compactification + of affine Berkovich space `\CC_p`. Points are represented using the classification (due + to Berkovich) of a corresponding decreasing sequence of disks in `\CC_p`. AUTHORS: From 8f653acd83b507ef45e0a25ea60299008d37ff6d Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 3 Aug 2020 19:25:21 -0400 Subject: [PATCH 160/379] added hook product and started in ext computation --- src/sage/combinat/posets/d_complete.py | 9 ++++ src/sage/combinat/posets/linear_extensions.py | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/sage/combinat/posets/d_complete.py b/src/sage/combinat/posets/d_complete.py index ef4e85f8324..02ffb8b043f 100644 --- a/src/sage/combinat/posets/d_complete.py +++ b/src/sage/combinat/posets/d_complete.py @@ -20,6 +20,7 @@ from .linear_extensions import LinearExtensionsOfPosetWithHooks from .lattices import FiniteJoinSemilattice from collections import deque +from sage.misc.misc_c import prod class DCompletePoset(FiniteJoinSemilattice): r""" @@ -153,3 +154,11 @@ def get_hooks(self): """ return dict(self._hooks) + def hook_product(self): + r""" + Return the hook product for the poset. + """ + if self._poset.cardinality() == 0: + return 1 + + return prod(self._hooks.values()) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 60dbf6ff4a4..0e4cf8968b4 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -38,6 +38,7 @@ from sage.structure.list_clone import ClonableArray from sage.misc.misc_c import prod from sage.functions.other import factorial +from sage.combinat.posets.d_complete import DCompletePoset class LinearExtensionOfPoset(ClonableArray, @@ -893,4 +894,51 @@ def cardinality(self): 140 """ return sum(self.atkinson(self._elements[0])) + +class LinearExtensionsOfMobile(LinearExtensionOfPoset): + r""" + Linear extensions for a mobile poset. + """ + def cardinality(self): + r""" + Return the number of linear extensions by using the determinant + formula for counting linear extensions of mobiles. + """ + + # Find folds + if self._poset._anchor: + anchor_index = self._poset._ribbon.indexOf(self._poset._anchor[0]) + else: + anchor_index = len(self._poset._ribbon) + + fold_up = [] + fold_down = [] + + for ind, r in enumerate(self._poset._ribbon[:-1]): + if ind < anchor_index and self._poset.is_greater_than(r, self._poset._ribbon[ind+1]): + fold_up.append((self._poset._ribbon[ind + 1], r)) + elif ind >= anchor_index and self._poset.is_less_than(r, self._poset._ribbon[ind+1]): + fold_down.append((r, self._poset._ribbon[ind+1])) + + folds = fold_up + fold_down + # Get ordered connected components + + cr = self._poset.cover_relations() + foldless_cr = [r for c in cr if (not r in folds)] + + elmts = self._poset._elements + + poset_components = DiGraph([elmts, foldless_cr]) + ordered_poset_components = list(map(lambda l: poset_components.connected_component_containing_vertex(l), [fold[0] for fold in folds])) + ordered_poset_components.append(poset_components.connected_component_containing_vertex(folds[-1][1])) + + # Return determinant + matrix = [] + for i in range(len(folds)): + mat_poset = DCompletePoset(self._poset.subposet(ordered_poset_components[i])) + row = [0] * (i-1 if i-1 > 0 else 0) + [1] * (1 if i >= 1 else 0) + for j,f in enumerate(folds[i:]): + row.append(1 / mat_poset.hook_product()) + + From 208b65deef86c308161ca85f7685167854a1bc1b Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 9 Aug 2020 16:00:29 -0400 Subject: [PATCH 161/379] added cardinality for Mobile posets --- src/sage/combinat/posets/linear_extensions.py | 45 +++--- src/sage/combinat/posets/mobile.py | 139 +++++++++--------- src/sage/combinat/posets/posets.py | 26 ++-- src/sage/graphs/generic_graph.py | 24 +-- src/sage/graphs/graph.py | 18 +-- 5 files changed, 131 insertions(+), 121 deletions(-) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 0e4cf8968b4..814a13f8cd4 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -38,7 +38,6 @@ from sage.structure.list_clone import ClonableArray from sage.misc.misc_c import prod from sage.functions.other import factorial -from sage.combinat.posets.d_complete import DCompletePoset class LinearExtensionOfPoset(ClonableArray, @@ -894,7 +893,7 @@ def cardinality(self): 140 """ return sum(self.atkinson(self._elements[0])) - + class LinearExtensionsOfMobile(LinearExtensionOfPoset): r""" Linear extensions for a mobile poset. @@ -904,41 +903,53 @@ def cardinality(self): Return the number of linear extensions by using the determinant formula for counting linear extensions of mobiles. """ - + import sage.combinat.posets.d_complete as dc + import sage.combinat.posets.posets as fp # Find folds if self._poset._anchor: anchor_index = self._poset._ribbon.indexOf(self._poset._anchor[0]) else: anchor_index = len(self._poset._ribbon) - + fold_up = [] fold_down = [] - + for ind, r in enumerate(self._poset._ribbon[:-1]): if ind < anchor_index and self._poset.is_greater_than(r, self._poset._ribbon[ind+1]): fold_up.append((self._poset._ribbon[ind + 1], r)) elif ind >= anchor_index and self._poset.is_less_than(r, self._poset._ribbon[ind+1]): fold_down.append((r, self._poset._ribbon[ind+1])) - + folds = fold_up + fold_down # Get ordered connected components - + cr = self._poset.cover_relations() foldless_cr = [r for c in cr if (not r in folds)] - + elmts = self._poset._elements - + poset_components = DiGraph([elmts, foldless_cr]) - ordered_poset_components = list(map(lambda l: poset_components.connected_component_containing_vertex(l), [fold[0] for fold in folds])) - ordered_poset_components.append(poset_components.connected_component_containing_vertex(folds[-1][1])) - + ordered_poset_components = list(map(lambda l: poset_components.connected_component_containing_vertex(l), + [fold[1] for fold in fold_up] + [fold[0] for fold in fold_down])) + ordered_poset_components.append(poset_components.connected_component_containing_vertex( + folds[-1][1] if len(fold_down) > 0 else folds[-1][0] + )) + # Return determinant - matrix = [] + mat = [] for i in range(len(folds)): - mat_poset = DCompletePoset(self._poset.subposet(ordered_poset_components[i])) + mat_poset = dc.DCompletePoset(self._poset.subposet(ordered_poset_components[i])) row = [0] * (i-1 if i-1 > 0 else 0) + [1] * (1 if i >= 1 else 0) - for j,f in enumerate(folds[i:]): + for j, f in enumerate(folds[i:]): row.append(1 / mat_poset.hook_product()) - - + if j + i < len(folds) - 1: + next_poset = self._poset.subposet(ordered_poset_components[j+i+1]) + + mat_poset = dc.DCompletePoset(fp.FinitePoset.slant_sum(mat_poset, next_poset, f[1], f[0])) + + mat.append(row) + + return matrix(QQ, mat).determinant() * factorial(self._poset.cardinality()) + + diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index 4844c9b2179..e6eec15aa68 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -11,31 +11,30 @@ from sage.combinat.posets.posets import Poset, FinitePoset from sage.combinat.posets.d_complete import DCompletePoset from sage.misc.lazy_attribute import lazy_attribute - -#from .linear_extensions import LinearExtensionsOfForest +from .linear_extensions import LinearExtensionsOfMobile class MobilePoset(FinitePoset): r""" Mobile posets are an extension of d-complete posets which permit a determinant - formula for counting linear extensions. + formula for counting linear extensions. """ - + #_lin_ext_type = LinearExtensionsOfForest _desc = 'Finite mobile poset' - - + + def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None): FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, category=category, facade=facade, key=key) if ribbon and self._test_valid_ribbon(ribbon): self._ribbon = ribbon - + def _test_valid_ribbon(self, ribbon): r""" Returns True if a ribbon has at most one anchor and that no vertex has two or more anchors - + INPUT: - - ``ribbon`` -- a list of elements that form a ribbon in your poset + - ``ribbon`` -- a list of elements that form a ribbon in your poset """ ribbon = list(map(lambda x: self._element_to_vertex(x), ribbon)) H = self._hasse_diagram @@ -47,22 +46,22 @@ def _test_valid_ribbon(self, ribbon): num_anchors += 1 elif len(anchor_neighbors) > 1: return False - + return num_anchors <= 1 - - + + @lazy_attribute def _anchor(self): r""" The anchor of the mobile poset. - + TESTS:: - + sage: from sage.combinat.posets.mobile import MobilePoset sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: M._anchor (4, 3) - sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) sage: M2._anchor (4, 3) sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) @@ -72,9 +71,9 @@ def _anchor(self): ribbon = list(map(lambda x: self._element_to_vertex(x), self._ribbon)) H = self._hasse_diagram R = H.subgraph(ribbon) - + anchor = None - + # Find the anchor vertex, if it exists, and return the edge for r in ribbon: anchor_neighbors = set(H.neighbors_out(r)).difference(set(R.neighbors_out(r))) @@ -82,23 +81,23 @@ def _anchor(self): anchor = (r, anchor_neighbors.pop()) break return (self._vertex_to_element(anchor[0]), self._vertex_to_element(anchor[1])) if not anchor is None else None - - - + + + @lazy_attribute def _ribbon(self): r""" The ribbon of the mobile poset. - + TESTS:: - + sage: from sage.combinat.posets.mobile import MobilePoset sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: sorted(M._ribbon) [4, 5, 6, 7, 8] sage: M._test_valid_ribbon(M._ribbon) True - sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) sage: sorted(M2._ribbon) [4, 7, 8] sage: M2._test_valid_ribbon(M2._ribbon) @@ -110,7 +109,7 @@ def _ribbon(self): True """ return list(map(lambda x: self._vertex_to_element(x), self._compute_ribbon())) - + def _compute_ribbon(self): r""" The helper function of _ribbon for computing the ribbon. @@ -118,23 +117,23 @@ def _compute_ribbon(self): H = self._hasse_diagram H_un = H.to_undirected() max_elmts = H.sinks() - + # Compute anchor, ribbon ribbon = [] # In order list of elements on zigzag - + if len(max_elmts) == 1: return [max_elmts[0]] # Compute max element tree by merging shortest paths start = max_elmts[0] - + zigzag_elmts = set() for m in max_elmts[1:]: sp = H_un.shortest_path(start, m) zigzag_elmts.update(sp) - + max_elmt_graph = H.subgraph(zigzag_elmts) G = max_elmt_graph.to_undirected() - + if G.is_path(): # Check if there is a anchor by seeing if there is more than one acyclic path to the next max ends = max_elmt_graph.vertices_with_degree(1) @@ -145,78 +144,78 @@ def _compute_ribbon(self): traverse_ribbon = ribbon if end_count == 0 else ribbon[::-1] for ind, p in enumerate(traverse_ribbon): if H_un.is_cut_edge(p, traverse_ribbon[ind+1]): - return G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1]) + return G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1]) return ribbon - - + + else: # First check path counts between ends and deg3 vertex - # Then check if more than one max elmt on way to degree 3 vertex. + # Then check if more than one max elmt on way to degree 3 vertex. # Arbitrarily choose between ones with just 1 - + ends = max_elmt_graph.vertices_with_degree(1) deg3 = max_elmt_graph.vertices_with_degree(3)[0] - + anchoredEnd = None for end in ends: if not (H_un.is_cut_vertex(end) or H_un.degree(end) == 1): anchoredEnd = end break - + if not anchoredEnd is None: path = H.shortest_path(deg3, anchoredEnd) ends.remove(anchoredEnd) - + return G.shortest_path(ends[0], ends[1]) - + else: possible_anchors = ends[:] for end in ends: path = G.shortest_path(end, deg3) if not sum(map(lambda z: z in max_elmts, path)) == 1: possible_anchors.remove(end) - + anchoredEnd = possible_anchors[0] ends.remove(anchoredEnd) return G.shortest_path(ends[0], ends[1]) - + def get_ribbon(self): r""" Returns the ribbon of the mobile poset """ return self._ribbon - + def get_anchor(self): r""" Returns the anchor of the mobile poset """ return self._anchor - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 45e3c82787d..487a611a93d 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -1657,7 +1657,7 @@ def spectrum(self, a): sage: P = posets.ChainPoset(5) sage: P.spectrum(2) [0, 0, 1, 0, 0] - + sage: P = posets.BooleanLattice(3) sage: P.spectrum(5) [0, 0, 0, 4, 12, 16, 16, 0] @@ -1751,7 +1751,7 @@ def atkinson(self, a): components = self.connected_components() remainder_poset = Poset() - + for X in components: if a in X: main = X @@ -2373,22 +2373,22 @@ def is_d_complete(self): break return True - + @staticmethod def slant_sum(P1, P2, P1_element, P2_element): r""" Return the slant sum poset of posets P1 and P2 by connecting them with a cover relation (P1_element, P2_element). Element names of P1 and P2 must be distinct. - - INPUT: - + + INPUT: + - ``P1`` -- The first poset - ``P2`` -- The second poset - ``P1_element`` -- The element of P1 that is the bottom of the new cover relation - ``P2_element`` -- The element of P2 that is the top of the new cover relation - + EXAMPLES:: - + sage: from sage.combinat.posets.posets import FinitePoset sage: R = Posets.RibbonPoset(5, [1,2]) sage: H = Poset([[5, 6, 7], [(5, 6), (6,7)]]) @@ -2400,15 +2400,15 @@ def slant_sum(P1, P2, P1_element, P2_element): cover_relations = P1.cover_relations() + P2.cover_relations() cover_relations.append((P1_element, P2_element)) return Poset([elements, cover_relations]) - + @staticmethod def multislant_sum(psets, relations): r""" Return the union of posets provided in ``psets``, along with the additional cover relations specified in ``relations`` - + INPUT: - + - ``psets`` -- A list of finite posets - ``relations`` -- A list of new cover relations """ @@ -2417,10 +2417,10 @@ def multislant_sum(psets, relations): for p in psets: elements.extend(p._elements) cover_relations.extend(p.cover_relations()) - + for r in relations: cover_relations.append(r) - + return Poset([elements, cover_relations]) def intervals_poset(self): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 8963bb2f3ba..09de7fa6f3c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12083,31 +12083,31 @@ def degree(self, vertices=None, labels=False): return next(self.degree_iterator(vertices, labels)) else: return list(self.degree_iterator(vertices, labels)) - + def vertices_with_degree(self, degree): r""" Return a list of vertices with the indicated degree. - + INPUT: - ``degree`` -- a degree to match with each vertex in the graph - + EXAMPLES: - - sage: G = graphs.CompleteGraph(5) - sage: G.vertices_with_degree(4) - [0, 1, 2, 3, 4] - sage: P = graphs.PathGraph(5) - sage: P.vertices_with_degree(1) - [0, 4] + + sage: G = graphs.CompleteGraph(5) + sage: G.vertices_with_degree(4) + [0, 1, 2, 3, 4] + sage: P = graphs.PathGraph(5) + sage: P.vertices_with_degree(1) + [0, 4] """ vertices = [] for v in self.vertex_iterator(): if self.degree(v) == degree: vertices.append(v) - + return vertices - + def average_degree(self): r""" Return the average degree of the graph. diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 0021a45422e..109ed4dcb50 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -4836,20 +4836,20 @@ def maximum_average_degree(self, value_only=True, solver=None, verbose=0): return g_mad.average_degree() else: return g_mad - + @doc_index("Graph properties") def is_path(self): r""" Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). - + EXAMPLES: - - sage: G = graphs.PathGraph(5) - sage: G.is_path() - True - sage: H = graphs.CycleGraph(5) - sage: H.is_path() - False + + sage: G = graphs.PathGraph(5) + sage: G.is_path() + True + sage: H = graphs.CycleGraph(5) + sage: H.is_path() + False """ ds = self.degree_sequence() return all(map(lambda x: x[0] == x[1], zip(ds, ([2] * (self.order() - 2) + ([1] * 2))))) From f5aadaba8843615249b6a39231b0d57fa85e395a Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 9 Aug 2020 17:15:01 -0400 Subject: [PATCH 162/379] changed degree_iterator and added vertices_with_degree and is_path --- src/sage/graphs/generic_graph.py | 32 ++++++++++++++++++++++++++++---- src/sage/graphs/graph.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 0c40b5f68ee..3a130d5bdc9 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12084,6 +12084,30 @@ def degree(self, vertices=None, labels=False): else: return list(self.degree_iterator(vertices, labels)) + def vertices_with_degree(self, degree): + r""" + Return a list of vertices with the indicated degree. + + INPUT: + + - ``degree`` -- a degree to match with each vertex in the graph + + EXAMPLES: + + sage: G = graphs.CompleteGraph(5) + sage: G.vertices_with_degree(4) + [0, 1, 2, 3, 4] + sage: P = graphs.PathGraph(5) + sage: P.vertices_with_degree(1) + [0, 4] + """ + vertices = [] + for v, d in zip(self, self.degree_iterator()): + if d == degree: + vertices.append(v) + + return vertices + def average_degree(self): r""" Return the average degree of the graph. @@ -12226,11 +12250,11 @@ def degree_iterator(self, vertices=None, labels=False): else: vertices = [v for v in vertices if v in self] if labels: - filter_ = lambda v, self: (v, self._backend.degree(v, self._directed)) + for v in vertices: + yield (v, self._backend.degree(v, self._directed)) else: - filter_ = lambda v, self: self._backend.degree(v, self._directed) - for v in vertices: - yield filter_(v, self) + for v in vertices: + yield self._backend.degree(v, self._directed) def degree_sequence(self): r""" diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 9de2c628e93..92b6215748c 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3231,6 +3231,37 @@ def is_semi_symmetric(self): self.is_edge_transitive() and not self.is_vertex_transitive()) + @doc_index("Graph properties") + def is_path(self): + r""" + Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). + + EXAMPLES: + + sage: G = graphs.PathGraph(5) + sage: G.is_path() + True + sage: H = graphs.CycleGraph(5) + sage: H.is_path() + False + """ + deg_one_counter = 0 + seen_counter = 0 + for v in self.depth_first_search(self.vertices()[0]): + seen_counter += 1 + + if deg_one_counter > 2: + return False + + if self.degree(v) == 1: + deg_one_counter += 1 + + elif self.degree(v) != 2: + return False + + return seen_counter == self.order() and deg_one_counter == 2 + + @doc_index("Connectivity, orientations, trees") def degree_constrained_subgraph(self, bounds, solver=None, verbose=0): r""" From 9bc3290906bde6515f8b933a1de0c6d3eeaec754 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 9 Aug 2020 17:27:49 -0400 Subject: [PATCH 163/379] added mobile linear extension examples --- src/sage/combinat/posets/linear_extensions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 814a13f8cd4..fc105f6e56c 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -902,6 +902,18 @@ def cardinality(self): r""" Return the number of linear extensions by using the determinant formula for counting linear extensions of mobiles. + + EXAMPLES:: + + sage: from sage.combinat.posets.mobile import MobilePoset + sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4, + ....: 3), (5,4),(5,6),(7,4),(7,8)]])) + sage: M.linear_extensions().cardinality() + 1098 + + sage: M1 = posets.RibbonPoset(6, [1,3]) + sage: M1.linear_extensions().cardinality() + 61 """ import sage.combinat.posets.d_complete as dc import sage.combinat.posets.posets as fp From d0c46489b459f0da08c2a88dcee7be750227c427 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sun, 9 Aug 2020 22:11:14 -0600 Subject: [PATCH 164/379] Fix docs --- src/sage/combinat/fully_commutative_elements.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 86e04def474..3c3a772fc73 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -288,8 +288,9 @@ def plot_heap(self): Graphics object consisting of 15 graphics primitives .. PLOT:: + :width: 400px - FC = FullyCommutativeElements(['B', 5]) + FC = CoxeterGroup(['B', 5]).fully_commutative_elements() g = FC([3,2,4,3,1]).plot_heap() sphinx_plot(g) """ @@ -1048,7 +1049,7 @@ def iterate_to_length(self, length): INPUT: - - ``length`` -- integer; maximum length of element to generate. + - ``length`` -- integer; maximum length of element to generate OUTPUT: generator for elements of ``self`` of length up to ``length`` From a91e1ca17042e4c9fdbfaa7c54b4c1c876480cf3 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sun, 9 Aug 2020 22:11:29 -0600 Subject: [PATCH 165/379] Clean up iterator --- src/sage/combinat/fully_commutative_elements.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 3c3a772fc73..aa7568be35c 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -1026,22 +1026,18 @@ def __iter__(self): # set, for the keys are then ordered by default by Python 3.7+: recent_words = {empty_word: True} yield empty_word - length = 1 - while True: + while recent_words: new_words = {} - for w in recent_words.keys(): + for w in recent_words: for s in letters: if w.still_reduced_fc_after_prepending(s): sw = self.element_class( self, [s] + list(w), check=False) # "Add" sw to the "set" new_words[sw] = True - if len(new_words) == 0: - return - for w in new_words.keys(): + for w in new_words: yield w recent_words = new_words - length += 1 def iterate_to_length(self, length): r""" From a50966505c8dab8063b07aef349c9b7b856c09ce Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Sun, 9 Aug 2020 22:12:13 -0600 Subject: [PATCH 166/379] Standardize class inheritance; remove length assertion. --- src/sage/combinat/fully_commutative_elements.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index aa7568be35c..1ecb3e3e03e 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -775,7 +775,7 @@ def star_operation(self, J, direction, side='left'): return self.parent().element_class(self.parent(), combined_data, check=False) -class FullyCommutativeElements(Parent, UniqueRepresentation): +class FullyCommutativeElements(UniqueRepresentation, Parent): r""" Class for the set of fully commutative (FC) elements of a Coxeter system. @@ -1082,7 +1082,6 @@ def iterate_to_length(self, length): 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], [1, 0, 2, 1], [2, 1, 0, 2], [2, 0, 1, 2]] """ - assert length >= 0 for w in self: if len(w) > length: break From b174c03c6cccc39e46b316c132db286af78df461 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Mon, 10 Aug 2020 11:11:31 -0600 Subject: [PATCH 167/379] Patch up FC-finite/infinite detection. --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 1ecb3e3e03e..e4b31293423 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -955,7 +955,7 @@ def __init__(self, coxeter_group): # All finite Coxeter groups are certainly FC-finite. Of the affine Coxeter groups only # the groups affine `F_4` and affine `E_8` are FC-finite; they have rank 5 and rank 9 # and correspond to the groups `F_5` and `E_9` in [Ste1996]_, respectively: - if not ctype.is_affine() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9): + if ctype.is_finite() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9): category = category.Finite() else: category = category.Infinite() From fee70cbb26b64fa9181e3baced0c628dc63a8971 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Mon, 10 Aug 2020 19:53:14 -0600 Subject: [PATCH 168/379] Fix docs --- src/sage/categories/coxeter_groups.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index bbd7a5b0557..eec89a37d0d 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -608,10 +608,11 @@ def grassmannian_elements(self, side="right"): def fully_commutative_elements(self): r""" - Return the combinatorial class of fully commutative elements in this - Coxeter group. See - :class:`~sage.combinat.fully_commutative_elements.FullyCommutativeElements` - for details. + Return the set of fully commutative elements in this Coxeter group. + + .. SEEALSO:: + + :class:`~sage.combinat.fully_commutative_elements.FullyCommutativeElements` EXAMPLES:: From 64f22b2cff0e12dde96e35a209b6ca13b197997b Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Mon, 10 Aug 2020 19:53:38 -0600 Subject: [PATCH 169/379] Use standard Sage copyright --- src/sage/combinat/fully_commutative_elements.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index e4b31293423..66912764484 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -17,13 +17,16 @@ Colorado Boulder. We thank Rachel Castro, Joel Courtney, Thomas Magnuson and Natalie Schoenhals for their contribution to the project and the code. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2020 Chase Meadors , # Tianyuan Xu # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# 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.parent import Parent from sage.structure.list_clone import NormalizedClonableList from sage.categories.enumerated_sets import EnumeratedSets From b974c6ebdabf601a5e1c57cfc6eed1497489875f Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 11 Aug 2020 04:17:08 -0600 Subject: [PATCH 170/379] Redesign FC-finite/infinite detection --- .../combinat/fully_commutative_elements.py | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 66912764484..994197148a3 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -953,19 +953,25 @@ def __init__(self, coxeter_group): ctype = self._coxeter_group.coxeter_type() try: - # A 'letter' family is defined if and only if the Coxeter group is finite or affine: family, rank = ctype.type(), ctype.rank() - # All finite Coxeter groups are certainly FC-finite. Of the affine Coxeter groups only - # the groups affine `F_4` and affine `E_8` are FC-finite; they have rank 5 and rank 9 - # and correspond to the groups `F_5` and `E_9` in [Ste1996]_, respectively: - if ctype.is_finite() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9): - category = category.Finite() - else: - category = category.Infinite() except AttributeError: - # No refinement is specified for Coxeter groups that are not finite or affine - # (Note that this includes groups of the form E_n (n>9), F_n (n>5) and H_n (n>4) - # from [Ste1996]_ which are known to be FC-finite) + family, rank = None, None + + if ctype.is_finite(): + # Finite Coxeter groups are certainly FC-finite + category = category.Finite() + elif (family == 'F' and rank == 5) or (family == 'E' and rank == 9): + # Of the affine Coxeter groups only the groups affine `F_4` and + # affine `E_8` are FC-finite; they have rank 5 and rank 9 and + # correspond to the groups `F_5` and `E_9` in [Ste1996]_. + category = category.Finite() + elif family is not None and family != 'reducible': + # This covers all remaining affine types that are not the two above. + category = category.Infinite() + else: + # Specify no refinement for reducible types or indefinite types + # (Note that this includes groups of the form E_n (n>9), F_n (n>5) + # and H_n (n>4) from [Ste1996]_ which are known to be FC-finite) pass Parent.__init__(self, category=category) From 614bed917bf2df22ced4e6056c11fcb80d9e5763 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 11 Aug 2020 11:17:32 -0400 Subject: [PATCH 171/379] 29844: fixed equality for Berkovich Space, added hashing for Berkovich Space, and added test suites --- .../schemes/berkovich/berkovich_cp_element.py | 5 +++ src/sage/schemes/berkovich/berkovich_space.py | 40 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index aa5c44615ad..b5ad04be3eb 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -1225,6 +1225,8 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: B = Berkovich_Cp_Affine(5) sage: B(w, power=1) Type II point centered at w + O(w^41) of radius 5^1 + + sage: TestSuite(Q5).run() """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): """ @@ -1858,6 +1860,9 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): ... ValueError: type II and III points can not be centered at infinity + sage: B = Berkovich_Cp_Projective(3) + sage: Q1 = B(3) + sage: TestSuite(Q1).run() """ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): """ diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 0c19879e0f0..ab648148310 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -215,6 +215,13 @@ def __eq__(self,right): sage: D = Berkovich_Cp_Affine(B, B_ideal) sage: C == D False + + :: + + sage: A_ideal_2 = A.prime_above(5) + sage: E = Berkovich_Cp_Affine(A, A_ideal_2) + sage: C == E + False """ if not isinstance(right, Berkovich_Cp): return False @@ -223,7 +230,7 @@ def __eq__(self,right): if self._base_type == 'padic field': return self.prime() == right.prime() else: - return self.base() == right.base() + return self.base() == right.base() and self.ideal() == right.ideal() def __ne__(self,right): """ @@ -239,6 +246,27 @@ def __ne__(self,right): """ return not(self == right) + def __hash__(self): + """ + Hash function. + + EXAMPLES:: + + sage: hash(Berkovich_Cp_Projective(3)) + 3 + + :: + + sage: R. = QQ[] + sage: A. = NumberField(z^2 + 1) + sage: B = Berkovich_Cp_Projective(A, A.prime_above(5)) + sage: hash(B) + 1629824360745675264 + """ + if self._base_type == 'padic field': + return hash(self.prime()) + return hash(self.ideal()) + class Berkovich_Cp_Affine(Berkovich_Cp): r""" The Berkovich affine line over `\CC_p`. @@ -355,6 +383,11 @@ class Berkovich_Cp_Affine(Berkovich_Cp): sage: A. = AffineSpace(Qp(3), 1) sage: Berkovich_Cp_Affine(A) Affine Berkovich line over Cp(3) of precision 20 + + :: + + sage: B = Berkovich_Cp_Projective(3) + sage: TestSuite(B).run() """ Element = Berkovich_Element_Cp_Affine @@ -534,6 +567,11 @@ class Berkovich_Cp_Projective(Berkovich_Cp): ... TypeError: could not convert c to Projective Space of dimension 1 over Number Field in a with defining polynomial x^3 + 20 + + TESTS:: + + sage: B = Berkovich_Cp_Projective(3) + sage: TestSuite(B).run() """ Element = Berkovich_Element_Cp_Projective From 311d8a1011c759ee8120ea0ba189d3226d2c09ed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 9 Mar 2020 22:45:42 -0400 Subject: [PATCH 172/379] Add Dockerfile generator, basic package information for nix --- build/bin/write-dockerfile.sh | 26 +++++++++++++++++++------- build/pkgs/nix-bootstrap.txt | 2 ++ build/pkgs/nix.txt | 22 ++++++++++++++++++++++ tox.ini | 5 +++++ 4 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 build/pkgs/nix-bootstrap.txt create mode 100644 build/pkgs/nix.txt diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 3100565f8fa..2de2c771e0a 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -91,6 +91,18 @@ EOF EXISTS="pacman -Si" INSTALL="pacman -Su --noconfirm" ;; + nix*) + # https://hub.docker.com/r/nixos/nix + cat < Date: Tue, 11 Aug 2020 11:33:18 -0700 Subject: [PATCH 173/379] build/bin/write-dockerfile.sh [nix]: Fixup --- build/bin/write-dockerfile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index d95c6d08060..785f1c25e05 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -95,7 +95,7 @@ EOF # https://hub.docker.com/r/nixos/nix cat < Date: Tue, 11 Aug 2020 11:56:50 -0700 Subject: [PATCH 174/379] build/pkgs/*/distros/nix.txt: Add from sagelib.nix --- build/pkgs/arb/distros/nix.txt | 1 + build/pkgs/boost/distros/nix.txt | 1 + build/pkgs/cliquer/distros/nix.txt | 1 + build/pkgs/ecl/distros/nix.txt | 1 + build/pkgs/eclib/distros/nix.txt | 1 + build/pkgs/ecm/distros/nix.txt | 1 + build/pkgs/fflas_ffpack/distros/nix.txt | 1 + build/pkgs/flint/distros/nix.txt | 1 + build/pkgs/gap/distros/nix.txt | 1 + build/pkgs/givaro/distros/nix.txt | 1 + build/pkgs/glpk/distros/nix.txt | 1 + build/pkgs/gsl/distros/nix.txt | 1 + build/pkgs/iml/distros/nix.txt | 1 + build/pkgs/lcalc/distros/nix.txt | 1 + build/pkgs/libbraiding/distros/nix.txt | 1 + build/pkgs/libgd/distros/nix.txt | 1 + build/pkgs/libhomfly/distros/nix.txt | 1 + build/pkgs/linbox/distros/nix.txt | 1 + build/pkgs/lrcalc/distros/nix.txt | 1 + build/pkgs/m4ri/distros/nix.txt | 1 + build/pkgs/m4rie/distros/nix.txt | 1 + build/pkgs/mpc/distros/nix.txt | 1 + build/pkgs/mpfi/distros/nix.txt | 1 + build/pkgs/ntl/distros/nix.txt | 1 + build/pkgs/openblas/distros/nix.txt | 2 ++ build/pkgs/pari/distros/nix.txt | 1 + build/pkgs/planarity/distros/nix.txt | 1 + build/pkgs/ppl/distros/nix.txt | 1 + build/pkgs/ratpoints/distros/nix.txt | 1 + build/pkgs/readline/distros/nix.txt | 1 + build/pkgs/rw/distros/nix.txt | 1 + build/pkgs/singular/distros/nix.txt | 1 + build/pkgs/symmetrica/distros/nix.txt | 1 + build/pkgs/zn_poly/distros/nix.txt | 1 + 34 files changed, 35 insertions(+) create mode 100644 build/pkgs/arb/distros/nix.txt create mode 100644 build/pkgs/boost/distros/nix.txt create mode 100644 build/pkgs/cliquer/distros/nix.txt create mode 100644 build/pkgs/ecl/distros/nix.txt create mode 100644 build/pkgs/eclib/distros/nix.txt create mode 100644 build/pkgs/ecm/distros/nix.txt create mode 100644 build/pkgs/fflas_ffpack/distros/nix.txt create mode 100644 build/pkgs/flint/distros/nix.txt create mode 100644 build/pkgs/gap/distros/nix.txt create mode 100644 build/pkgs/givaro/distros/nix.txt create mode 100644 build/pkgs/glpk/distros/nix.txt create mode 100644 build/pkgs/gsl/distros/nix.txt create mode 100644 build/pkgs/iml/distros/nix.txt create mode 100644 build/pkgs/lcalc/distros/nix.txt create mode 100644 build/pkgs/libbraiding/distros/nix.txt create mode 100644 build/pkgs/libgd/distros/nix.txt create mode 100644 build/pkgs/libhomfly/distros/nix.txt create mode 100644 build/pkgs/linbox/distros/nix.txt create mode 100644 build/pkgs/lrcalc/distros/nix.txt create mode 100644 build/pkgs/m4ri/distros/nix.txt create mode 100644 build/pkgs/m4rie/distros/nix.txt create mode 100644 build/pkgs/mpc/distros/nix.txt create mode 100644 build/pkgs/mpfi/distros/nix.txt create mode 100644 build/pkgs/ntl/distros/nix.txt create mode 100644 build/pkgs/openblas/distros/nix.txt create mode 100644 build/pkgs/pari/distros/nix.txt create mode 100644 build/pkgs/planarity/distros/nix.txt create mode 100644 build/pkgs/ppl/distros/nix.txt create mode 100644 build/pkgs/ratpoints/distros/nix.txt create mode 100644 build/pkgs/readline/distros/nix.txt create mode 100644 build/pkgs/rw/distros/nix.txt create mode 100644 build/pkgs/singular/distros/nix.txt create mode 100644 build/pkgs/symmetrica/distros/nix.txt create mode 100644 build/pkgs/zn_poly/distros/nix.txt diff --git a/build/pkgs/arb/distros/nix.txt b/build/pkgs/arb/distros/nix.txt new file mode 100644 index 00000000000..86c41dbaa5f --- /dev/null +++ b/build/pkgs/arb/distros/nix.txt @@ -0,0 +1 @@ +arb diff --git a/build/pkgs/boost/distros/nix.txt b/build/pkgs/boost/distros/nix.txt new file mode 100644 index 00000000000..d579dbe4edb --- /dev/null +++ b/build/pkgs/boost/distros/nix.txt @@ -0,0 +1 @@ +boost diff --git a/build/pkgs/cliquer/distros/nix.txt b/build/pkgs/cliquer/distros/nix.txt new file mode 100644 index 00000000000..524938db8e3 --- /dev/null +++ b/build/pkgs/cliquer/distros/nix.txt @@ -0,0 +1 @@ +cliquer diff --git a/build/pkgs/ecl/distros/nix.txt b/build/pkgs/ecl/distros/nix.txt new file mode 100644 index 00000000000..100aa2efb32 --- /dev/null +++ b/build/pkgs/ecl/distros/nix.txt @@ -0,0 +1 @@ +ecl diff --git a/build/pkgs/eclib/distros/nix.txt b/build/pkgs/eclib/distros/nix.txt new file mode 100644 index 00000000000..1fa444a63e6 --- /dev/null +++ b/build/pkgs/eclib/distros/nix.txt @@ -0,0 +1 @@ +eclib diff --git a/build/pkgs/ecm/distros/nix.txt b/build/pkgs/ecm/distros/nix.txt new file mode 100644 index 00000000000..e0a3513a5b1 --- /dev/null +++ b/build/pkgs/ecm/distros/nix.txt @@ -0,0 +1 @@ +ecm diff --git a/build/pkgs/fflas_ffpack/distros/nix.txt b/build/pkgs/fflas_ffpack/distros/nix.txt new file mode 100644 index 00000000000..683f84a42ca --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/nix.txt @@ -0,0 +1 @@ +fflas-ffpack diff --git a/build/pkgs/flint/distros/nix.txt b/build/pkgs/flint/distros/nix.txt new file mode 100644 index 00000000000..61c2ffe155a --- /dev/null +++ b/build/pkgs/flint/distros/nix.txt @@ -0,0 +1 @@ +flint diff --git a/build/pkgs/gap/distros/nix.txt b/build/pkgs/gap/distros/nix.txt new file mode 100644 index 00000000000..ea4feae733f --- /dev/null +++ b/build/pkgs/gap/distros/nix.txt @@ -0,0 +1 @@ +gap diff --git a/build/pkgs/givaro/distros/nix.txt b/build/pkgs/givaro/distros/nix.txt new file mode 100644 index 00000000000..54f01b4305f --- /dev/null +++ b/build/pkgs/givaro/distros/nix.txt @@ -0,0 +1 @@ +givaro diff --git a/build/pkgs/glpk/distros/nix.txt b/build/pkgs/glpk/distros/nix.txt new file mode 100644 index 00000000000..aca7917cfa1 --- /dev/null +++ b/build/pkgs/glpk/distros/nix.txt @@ -0,0 +1 @@ +glpk diff --git a/build/pkgs/gsl/distros/nix.txt b/build/pkgs/gsl/distros/nix.txt new file mode 100644 index 00000000000..bd0d9198bf3 --- /dev/null +++ b/build/pkgs/gsl/distros/nix.txt @@ -0,0 +1 @@ +gsl diff --git a/build/pkgs/iml/distros/nix.txt b/build/pkgs/iml/distros/nix.txt new file mode 100644 index 00000000000..c1773871ebc --- /dev/null +++ b/build/pkgs/iml/distros/nix.txt @@ -0,0 +1 @@ +iml diff --git a/build/pkgs/lcalc/distros/nix.txt b/build/pkgs/lcalc/distros/nix.txt new file mode 100644 index 00000000000..421591fc62d --- /dev/null +++ b/build/pkgs/lcalc/distros/nix.txt @@ -0,0 +1 @@ +lcalc diff --git a/build/pkgs/libbraiding/distros/nix.txt b/build/pkgs/libbraiding/distros/nix.txt new file mode 100644 index 00000000000..3767599b368 --- /dev/null +++ b/build/pkgs/libbraiding/distros/nix.txt @@ -0,0 +1 @@ +libbraiding diff --git a/build/pkgs/libgd/distros/nix.txt b/build/pkgs/libgd/distros/nix.txt new file mode 100644 index 00000000000..3f310cfdeb0 --- /dev/null +++ b/build/pkgs/libgd/distros/nix.txt @@ -0,0 +1 @@ +gd diff --git a/build/pkgs/libhomfly/distros/nix.txt b/build/pkgs/libhomfly/distros/nix.txt new file mode 100644 index 00000000000..4da5d0c664a --- /dev/null +++ b/build/pkgs/libhomfly/distros/nix.txt @@ -0,0 +1 @@ +libhomfly diff --git a/build/pkgs/linbox/distros/nix.txt b/build/pkgs/linbox/distros/nix.txt new file mode 100644 index 00000000000..891a35cb224 --- /dev/null +++ b/build/pkgs/linbox/distros/nix.txt @@ -0,0 +1 @@ +linbox diff --git a/build/pkgs/lrcalc/distros/nix.txt b/build/pkgs/lrcalc/distros/nix.txt new file mode 100644 index 00000000000..b854d5c73dd --- /dev/null +++ b/build/pkgs/lrcalc/distros/nix.txt @@ -0,0 +1 @@ +lrcalc diff --git a/build/pkgs/m4ri/distros/nix.txt b/build/pkgs/m4ri/distros/nix.txt new file mode 100644 index 00000000000..414704b4ece --- /dev/null +++ b/build/pkgs/m4ri/distros/nix.txt @@ -0,0 +1 @@ +m4ri diff --git a/build/pkgs/m4rie/distros/nix.txt b/build/pkgs/m4rie/distros/nix.txt new file mode 100644 index 00000000000..1ed8956ad8d --- /dev/null +++ b/build/pkgs/m4rie/distros/nix.txt @@ -0,0 +1 @@ +m4rie diff --git a/build/pkgs/mpc/distros/nix.txt b/build/pkgs/mpc/distros/nix.txt new file mode 100644 index 00000000000..098b049316b --- /dev/null +++ b/build/pkgs/mpc/distros/nix.txt @@ -0,0 +1 @@ +libmpc diff --git a/build/pkgs/mpfi/distros/nix.txt b/build/pkgs/mpfi/distros/nix.txt new file mode 100644 index 00000000000..0508439baac --- /dev/null +++ b/build/pkgs/mpfi/distros/nix.txt @@ -0,0 +1 @@ +mpfi diff --git a/build/pkgs/ntl/distros/nix.txt b/build/pkgs/ntl/distros/nix.txt new file mode 100644 index 00000000000..9f4d4f8fdb6 --- /dev/null +++ b/build/pkgs/ntl/distros/nix.txt @@ -0,0 +1 @@ +ntl diff --git a/build/pkgs/openblas/distros/nix.txt b/build/pkgs/openblas/distros/nix.txt new file mode 100644 index 00000000000..744c36bbb99 --- /dev/null +++ b/build/pkgs/openblas/distros/nix.txt @@ -0,0 +1,2 @@ +blas +lapack diff --git a/build/pkgs/pari/distros/nix.txt b/build/pkgs/pari/distros/nix.txt new file mode 100644 index 00000000000..67abbc9dec6 --- /dev/null +++ b/build/pkgs/pari/distros/nix.txt @@ -0,0 +1 @@ +pari diff --git a/build/pkgs/planarity/distros/nix.txt b/build/pkgs/planarity/distros/nix.txt new file mode 100644 index 00000000000..1556d0f7767 --- /dev/null +++ b/build/pkgs/planarity/distros/nix.txt @@ -0,0 +1 @@ +planarity diff --git a/build/pkgs/ppl/distros/nix.txt b/build/pkgs/ppl/distros/nix.txt new file mode 100644 index 00000000000..0efaae6634f --- /dev/null +++ b/build/pkgs/ppl/distros/nix.txt @@ -0,0 +1 @@ +ppl diff --git a/build/pkgs/ratpoints/distros/nix.txt b/build/pkgs/ratpoints/distros/nix.txt new file mode 100644 index 00000000000..e137758627f --- /dev/null +++ b/build/pkgs/ratpoints/distros/nix.txt @@ -0,0 +1 @@ +ratpoints diff --git a/build/pkgs/readline/distros/nix.txt b/build/pkgs/readline/distros/nix.txt new file mode 100644 index 00000000000..0b5a58e278a --- /dev/null +++ b/build/pkgs/readline/distros/nix.txt @@ -0,0 +1 @@ +readline diff --git a/build/pkgs/rw/distros/nix.txt b/build/pkgs/rw/distros/nix.txt new file mode 100644 index 00000000000..615dd1998af --- /dev/null +++ b/build/pkgs/rw/distros/nix.txt @@ -0,0 +1 @@ +rankwidth diff --git a/build/pkgs/singular/distros/nix.txt b/build/pkgs/singular/distros/nix.txt new file mode 100644 index 00000000000..5f0dc01955f --- /dev/null +++ b/build/pkgs/singular/distros/nix.txt @@ -0,0 +1 @@ +singular diff --git a/build/pkgs/symmetrica/distros/nix.txt b/build/pkgs/symmetrica/distros/nix.txt new file mode 100644 index 00000000000..27c7a2636ab --- /dev/null +++ b/build/pkgs/symmetrica/distros/nix.txt @@ -0,0 +1 @@ +symmetrica diff --git a/build/pkgs/zn_poly/distros/nix.txt b/build/pkgs/zn_poly/distros/nix.txt new file mode 100644 index 00000000000..5ff8997d672 --- /dev/null +++ b/build/pkgs/zn_poly/distros/nix.txt @@ -0,0 +1 @@ +zn_poly From 7c9bf8203abf1e9f01d6371934cb5761a206b5fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 Aug 2020 12:00:52 -0700 Subject: [PATCH 175/379] build/pkgs/*/distros/nix.txt: Add from sage-env.nix --- build/pkgs/flintqs/distros/nix.txt | 1 + build/pkgs/gfan/distros/nix.txt | 1 + build/pkgs/giac/distros/nix.txt | 1 + build/pkgs/jmol/distros/nix.txt | 1 + build/pkgs/maxima/distros/nix.txt | 1 + build/pkgs/nauty/distros/nix.txt | 1 + build/pkgs/palp/distros/nix.txt | 1 + build/pkgs/r/distros/nix.txt | 1 + build/pkgs/rubiks/distros/nix.txt | 1 + build/pkgs/sqlite/distros/nix.txt | 1 + build/pkgs/sympow/distros/nix.txt | 1 + build/pkgs/tachyon/distros/nix.txt | 1 + 12 files changed, 12 insertions(+) create mode 100644 build/pkgs/flintqs/distros/nix.txt create mode 100644 build/pkgs/gfan/distros/nix.txt create mode 100644 build/pkgs/giac/distros/nix.txt create mode 100644 build/pkgs/jmol/distros/nix.txt create mode 100644 build/pkgs/maxima/distros/nix.txt create mode 100644 build/pkgs/nauty/distros/nix.txt create mode 100644 build/pkgs/palp/distros/nix.txt create mode 100644 build/pkgs/r/distros/nix.txt create mode 100644 build/pkgs/rubiks/distros/nix.txt create mode 100644 build/pkgs/sqlite/distros/nix.txt create mode 100644 build/pkgs/sympow/distros/nix.txt create mode 100644 build/pkgs/tachyon/distros/nix.txt diff --git a/build/pkgs/flintqs/distros/nix.txt b/build/pkgs/flintqs/distros/nix.txt new file mode 100644 index 00000000000..87de35f2830 --- /dev/null +++ b/build/pkgs/flintqs/distros/nix.txt @@ -0,0 +1 @@ +flintqs diff --git a/build/pkgs/gfan/distros/nix.txt b/build/pkgs/gfan/distros/nix.txt new file mode 100644 index 00000000000..30698c7bd41 --- /dev/null +++ b/build/pkgs/gfan/distros/nix.txt @@ -0,0 +1 @@ +gfan diff --git a/build/pkgs/giac/distros/nix.txt b/build/pkgs/giac/distros/nix.txt new file mode 100644 index 00000000000..d3451656a62 --- /dev/null +++ b/build/pkgs/giac/distros/nix.txt @@ -0,0 +1 @@ +giac diff --git a/build/pkgs/jmol/distros/nix.txt b/build/pkgs/jmol/distros/nix.txt new file mode 100644 index 00000000000..f07a1f4e035 --- /dev/null +++ b/build/pkgs/jmol/distros/nix.txt @@ -0,0 +1 @@ +jmol diff --git a/build/pkgs/maxima/distros/nix.txt b/build/pkgs/maxima/distros/nix.txt new file mode 100644 index 00000000000..6400290f44d --- /dev/null +++ b/build/pkgs/maxima/distros/nix.txt @@ -0,0 +1 @@ +maxima-ecl diff --git a/build/pkgs/nauty/distros/nix.txt b/build/pkgs/nauty/distros/nix.txt new file mode 100644 index 00000000000..21c67b1e856 --- /dev/null +++ b/build/pkgs/nauty/distros/nix.txt @@ -0,0 +1 @@ +nauty diff --git a/build/pkgs/palp/distros/nix.txt b/build/pkgs/palp/distros/nix.txt new file mode 100644 index 00000000000..f037baef346 --- /dev/null +++ b/build/pkgs/palp/distros/nix.txt @@ -0,0 +1 @@ +palp diff --git a/build/pkgs/r/distros/nix.txt b/build/pkgs/r/distros/nix.txt new file mode 100644 index 00000000000..b0276f8a35e --- /dev/null +++ b/build/pkgs/r/distros/nix.txt @@ -0,0 +1 @@ +rWrapper diff --git a/build/pkgs/rubiks/distros/nix.txt b/build/pkgs/rubiks/distros/nix.txt new file mode 100644 index 00000000000..9991b8e7aa6 --- /dev/null +++ b/build/pkgs/rubiks/distros/nix.txt @@ -0,0 +1 @@ +rubiks diff --git a/build/pkgs/sqlite/distros/nix.txt b/build/pkgs/sqlite/distros/nix.txt new file mode 100644 index 00000000000..532c6c608dd --- /dev/null +++ b/build/pkgs/sqlite/distros/nix.txt @@ -0,0 +1 @@ +sqlite diff --git a/build/pkgs/sympow/distros/nix.txt b/build/pkgs/sympow/distros/nix.txt new file mode 100644 index 00000000000..a2ae7a8a59c --- /dev/null +++ b/build/pkgs/sympow/distros/nix.txt @@ -0,0 +1 @@ +sympow diff --git a/build/pkgs/tachyon/distros/nix.txt b/build/pkgs/tachyon/distros/nix.txt new file mode 100644 index 00000000000..39e8fd61a2d --- /dev/null +++ b/build/pkgs/tachyon/distros/nix.txt @@ -0,0 +1 @@ +tachyon From 0a639c4a399c4e58777d9f51e5e06fbd04b0e937 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 Aug 2020 12:50:39 -0700 Subject: [PATCH 176/379] build/pkgs/r/distros/nix.txt: rWrapper does not exist, use R --- build/pkgs/r/distros/nix.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/r/distros/nix.txt b/build/pkgs/r/distros/nix.txt index b0276f8a35e..331bae08fb7 100644 --- a/build/pkgs/r/distros/nix.txt +++ b/build/pkgs/r/distros/nix.txt @@ -1 +1 @@ -rWrapper +R From 0bd79113ab7bd6d0095761d2c08220511eee94b7 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 11 Aug 2020 19:27:20 -0600 Subject: [PATCH 177/379] Add more methods to CoxeterType --- src/sage/combinat/root_system/coxeter_type.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index 3ef254f28e5..4c29231d549 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -605,6 +605,59 @@ def is_simply_laced(self): """ return self._cartan_type.is_simply_laced() + def is_reducible(self): + """ + Return if ``self`` is reducible. + + EXAMPLES:: + + sage: C = CoxeterType(['A', 5]) + sage: C.is_reducible() + False + + sage: C = CoxeterType('A2xA2') + sage: C.is_reducible() + True + """ + return self._cartan_type.is_reducible() + + def is_irreducible(self): + """ + Return if ``self`` is irreducible. + + EXAMPLES:: + + sage: C = CoxeterType(['A', 5]) + sage: C.is_irreducible() + True + + sage: C = CoxeterType('B3xB3') + sage: C.is_irreducible() + False + """ + return self._cartan_type.is_irreducible() + + def component_types(self): + """ + A list of Coxeter types making up the reducible type. + + EXAMPLES:: + + sage: CoxeterType(['A',2],['B',2]).component_types() + [Coxeter type of ['A', 2], Coxeter type of ['B', 2]] + + sage: CoxeterType('A4xB3').component_types() + [Coxeter type of ['A', 4], Coxeter type of ['B', 3]] + + sage: CoxeterType(['A', 2]).component_types() + Traceback (most recent call last): + ... + ValueError: component types only defined for reducible types + """ + if self.is_irreducible(): + raise ValueError('component types only defined for reducible types') + return [CoxeterType(t) for t in self._cartan_type.component_types()] + def relabel(self, relabelling): """ Return a relabelled copy of ``self``. From 46e732a9ad77f7275cbf857616091a7bfcc4a37e Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Tue, 11 Aug 2020 19:27:29 -0600 Subject: [PATCH 178/379] Redesign FC-finite/infinite detection. --- .../combinat/fully_commutative_elements.py | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 994197148a3..58dc4097a37 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -951,27 +951,39 @@ def __init__(self, coxeter_group): # infinite enumerated sets for Coxeter groups of Cartan types. category = EnumeratedSets() - ctype = self._coxeter_group.coxeter_type() - try: - family, rank = ctype.type(), ctype.rank() - except AttributeError: - family, rank = None, None - - if ctype.is_finite(): - # Finite Coxeter groups are certainly FC-finite - category = category.Finite() - elif (family == 'F' and rank == 5) or (family == 'E' and rank == 9): - # Of the affine Coxeter groups only the groups affine `F_4` and - # affine `E_8` are FC-finite; they have rank 5 and rank 9 and - # correspond to the groups `F_5` and `E_9` in [Ste1996]_. - category = category.Finite() - elif family is not None and family != 'reducible': - # This covers all remaining affine types that are not the two above. - category = category.Infinite() + coxeter_type = self._coxeter_group.coxeter_type() + + if not isinstance(coxeter_type, CoxeterMatrix): + # This case handles all finite or affine Coxeter types (or products thereof) + ctypes = [coxeter_type] if coxeter_type.is_irreducible() else coxeter_type.component_types() + + is_finite = True + # this type will be FC-finite if and only if each component type is: + for ctype in ctypes: + family, rank = ctype.type(), ctype.rank() + if ctype.is_finite(): + # Finite Coxeter groups are certainly FC-finite + continue + elif (family == 'F' and rank == 5) or (family == 'E' and rank == 9): + # Of the affine Coxeter groups only the groups affine `F_4` and + # affine `E_8` are FC-finite; they have rank 5 and rank 9 and + # correspond to the groups `F_5` and `E_9` in [Ste1996]_. + continue + else: + # ctype is an affine group that is not one of the two in the + # previous case and is thus not FC-finite. + is_finite = False + break + + if is_finite: + category = category.Finite() + else: + category = category.Infinite() else: - # Specify no refinement for reducible types or indefinite types - # (Note that this includes groups of the form E_n (n>9), F_n (n>5) - # and H_n (n>4) from [Ste1996]_ which are known to be FC-finite) + # coxeter_type is a plain CoxeterMatrix, i.e. it is an indefinite + # type, and we do not specify any refinement. (Note that this + # includes groups of the form E_n (n>9), F_n (n>5) and H_n (n>4) + # from [Ste1996]_ which are known to be FC-finite). pass Parent.__init__(self, category=category) From c076383f03d7bc22d98a0a7b442f3e7b877799a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 Aug 2020 20:39:21 -0700 Subject: [PATCH 179/379] src/sage/interfaces/giac.py: Make new latex doctest more flexible --- src/sage/interfaces/giac.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 80bf35702cc..cc6b35421f0 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -1000,10 +1000,7 @@ def _latex_(self): \end{array}\right) sage: gM = giac(M) sage: latex(gM) - \left(\begin{array}{cc} - 1 & 2 \\ - 3 & 4 - \end{array}\right) + \left...\begin{array}{cc}...1...&...2...\\...3...&...4...\end{array}\right... sage: gf = giac('(x^4 - y)/(y^2-3*x)') sage: latex(gf) # output changed slightly from 1.5.0-63 to 1.5.0-87 \frac{...x^{4}...-...y...}{...y^{2}-3...x...} From 2bd2cb6dbd06103619e862998c0d5d06ebd9296c Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 12 Aug 2020 02:11:34 -0600 Subject: [PATCH 180/379] Make FC-finite/infinite detection more straightforward. --- src/sage/combinat/fully_commutative_elements.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 58dc4097a37..51d93cab4f7 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -961,17 +961,12 @@ def __init__(self, coxeter_group): # this type will be FC-finite if and only if each component type is: for ctype in ctypes: family, rank = ctype.type(), ctype.rank() - if ctype.is_finite(): - # Finite Coxeter groups are certainly FC-finite - continue - elif (family == 'F' and rank == 5) or (family == 'E' and rank == 9): + # Finite Coxeter groups are certainly FC-finite. # Of the affine Coxeter groups only the groups affine `F_4` and # affine `E_8` are FC-finite; they have rank 5 and rank 9 and # correspond to the groups `F_5` and `E_9` in [Ste1996]_. - continue - else: - # ctype is an affine group that is not one of the two in the - # previous case and is thus not FC-finite. + if not (ctype.is_finite() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9)): + # Finite Coxeter groups are certainly FC-finite is_finite = False break From 8263261b82f6c436ccf09fe0ba0d936a9aa31c50 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 12 Aug 2020 02:11:43 -0600 Subject: [PATCH 181/379] Add tests for cardinality detection. --- .../combinat/fully_commutative_elements.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 51d93cab4f7..5e1aa4b004c 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -890,6 +890,24 @@ class FullyCommutativeElements(UniqueRepresentation, Parent): Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(2)) [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2]] + + The cardinality of the set is determined from the classification of + FC-finite Coxeter groups:: + + sage: CoxeterGroup('A2').fully_commutative_elements().category() + Category of finite enumerated sets + sage: CoxeterGroup('B7').fully_commutative_elements().category() + Category of finite enumerated sets + sage: CoxeterGroup('A3~').fully_commutative_elements().category() + Category of infinite enumerated sets + sage: CoxeterGroup('F4~').fully_commutative_elements().category() + Category of finite enumerated sets + sage: CoxeterGroup('E8~').fully_commutative_elements().category() + Category of finite enumerated sets + sage: CoxeterGroup('F4~xE8~').fully_commutative_elements().category() + Category of finite enumerated sets + sage: CoxeterGroup('B4~xE8~').fully_commutative_elements().category() + Category of infinite enumerated sets """ @staticmethod def __classcall_private__(cls, data): @@ -962,9 +980,9 @@ def __init__(self, coxeter_group): for ctype in ctypes: family, rank = ctype.type(), ctype.rank() # Finite Coxeter groups are certainly FC-finite. - # Of the affine Coxeter groups only the groups affine `F_4` and - # affine `E_8` are FC-finite; they have rank 5 and rank 9 and - # correspond to the groups `F_5` and `E_9` in [Ste1996]_. + # Of the affine Coxeter groups only the groups affine `F_4` and + # affine `E_8` are FC-finite; they have rank 5 and rank 9 and + # correspond to the groups `F_5` and `E_9` in [Ste1996]_. if not (ctype.is_finite() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9)): # Finite Coxeter groups are certainly FC-finite is_finite = False From dd642fe4e334d98dba1c8ea37cf2e9802add78f4 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 12 Aug 2020 02:12:26 -0600 Subject: [PATCH 182/379] Remove extra comment. --- src/sage/combinat/fully_commutative_elements.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 5e1aa4b004c..40baf3f7bb9 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -984,7 +984,6 @@ def __init__(self, coxeter_group): # affine `E_8` are FC-finite; they have rank 5 and rank 9 and # correspond to the groups `F_5` and `E_9` in [Ste1996]_. if not (ctype.is_finite() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9)): - # Finite Coxeter groups are certainly FC-finite is_finite = False break From 884ca6663e3e18dd284774b1a3f51b30fdb50882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 13 Aug 2020 10:51:30 +1200 Subject: [PATCH 183/379] WIP: move sage-brial files and pbori.{pxd,pyx} in sage/rings/polynomial/pbori/ --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 116 +++ src/sage/rings/polynomial/pbori/__init__.py | 45 ++ src/sage/rings/polynomial/pbori/addition.py | 144 ++++ src/sage/rings/polynomial/pbori/blocks.py | 453 ++++++++++++ .../rings/polynomial/pbori/check_claims.py | 205 ++++++ src/sage/rings/polynomial/pbori/cluster.py | 120 ++++ src/sage/rings/polynomial/pbori/cnf.py | 241 +++++++ src/sage/rings/polynomial/pbori/coding.py | 91 +++ src/sage/rings/polynomial/pbori/context.py | 101 +++ .../polynomial/pbori/easy_polynomials.py | 49 ++ src/sage/rings/polynomial/pbori/fglm.py | 80 +++ src/sage/rings/polynomial/pbori/frontend.py | 88 +++ src/sage/rings/polynomial/pbori/gbcore.py | 676 ++++++++++++++++++ src/sage/rings/polynomial/pbori/gbrefs.py | 173 +++++ .../pbori/general_boolean_polynomial.py | 616 ++++++++++++++++ src/sage/rings/polynomial/pbori/heuristics.py | 36 + .../rings/polynomial/pbori/interpolate.py | 132 ++++ src/sage/rings/polynomial/pbori/interred.py | 29 + src/sage/rings/polynomial/pbori/intersect.py | 45 ++ src/sage/rings/polynomial/pbori/intpolys.py | 87 +++ src/sage/rings/polynomial/pbori/ll.py | 290 ++++++++ src/sage/rings/polynomial/pbori/memusage.py | 65 ++ src/sage/rings/polynomial/pbori/ncf.py | 41 ++ src/sage/rings/polynomial/pbori/nf.py | 673 +++++++++++++++++ src/sage/rings/polynomial/pbori/parallel.py | 288 ++++++++ src/sage/rings/polynomial/pbori/parsegat.py | 524 ++++++++++++++ src/sage/rings/polynomial/pbori/partial.py | 49 ++ .../rings/polynomial/{ => pbori}/pbori.pxd | 0 .../rings/polynomial/{ => pbori}/pbori.pyx | 0 src/sage/rings/polynomial/pbori/plot.py | 256 +++++++ src/sage/rings/polynomial/pbori/randompoly.py | 92 +++ src/sage/rings/polynomial/pbori/rank.py | 26 + src/sage/rings/polynomial/pbori/simplebb.py | 72 ++ .../rings/polynomial/pbori/specialsets.py | 103 +++ src/sage/rings/polynomial/pbori/statistics.py | 28 + 35 files changed, 6034 insertions(+) create mode 100644 src/sage/rings/polynomial/pbori/PyPolyBoRi.py create mode 100644 src/sage/rings/polynomial/pbori/__init__.py create mode 100644 src/sage/rings/polynomial/pbori/addition.py create mode 100644 src/sage/rings/polynomial/pbori/blocks.py create mode 100644 src/sage/rings/polynomial/pbori/check_claims.py create mode 100644 src/sage/rings/polynomial/pbori/cluster.py create mode 100644 src/sage/rings/polynomial/pbori/cnf.py create mode 100644 src/sage/rings/polynomial/pbori/coding.py create mode 100644 src/sage/rings/polynomial/pbori/context.py create mode 100644 src/sage/rings/polynomial/pbori/easy_polynomials.py create mode 100644 src/sage/rings/polynomial/pbori/fglm.py create mode 100644 src/sage/rings/polynomial/pbori/frontend.py create mode 100644 src/sage/rings/polynomial/pbori/gbcore.py create mode 100644 src/sage/rings/polynomial/pbori/gbrefs.py create mode 100644 src/sage/rings/polynomial/pbori/general_boolean_polynomial.py create mode 100644 src/sage/rings/polynomial/pbori/heuristics.py create mode 100644 src/sage/rings/polynomial/pbori/interpolate.py create mode 100644 src/sage/rings/polynomial/pbori/interred.py create mode 100644 src/sage/rings/polynomial/pbori/intersect.py create mode 100644 src/sage/rings/polynomial/pbori/intpolys.py create mode 100644 src/sage/rings/polynomial/pbori/ll.py create mode 100644 src/sage/rings/polynomial/pbori/memusage.py create mode 100644 src/sage/rings/polynomial/pbori/ncf.py create mode 100644 src/sage/rings/polynomial/pbori/nf.py create mode 100644 src/sage/rings/polynomial/pbori/parallel.py create mode 100644 src/sage/rings/polynomial/pbori/parsegat.py create mode 100644 src/sage/rings/polynomial/pbori/partial.py rename src/sage/rings/polynomial/{ => pbori}/pbori.pxd (100%) rename src/sage/rings/polynomial/{ => pbori}/pbori.pyx (100%) create mode 100644 src/sage/rings/polynomial/pbori/plot.py create mode 100644 src/sage/rings/polynomial/pbori/randompoly.py create mode 100644 src/sage/rings/polynomial/pbori/rank.py create mode 100644 src/sage/rings/polynomial/pbori/simplebb.py create mode 100644 src/sage/rings/polynomial/pbori/specialsets.py create mode 100644 src/sage/rings/polynomial/pbori/statistics.py diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py new file mode 100644 index 00000000000..0ad226d19a1 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -0,0 +1,116 @@ +"""PolyBoRi's interface to libpolybori* + +This file makes interfaces to PolyBoRi's runtime libraries available in Python via sage. + + +AUTHOR: + The PolyBoRi Team, 2007-2012 + + Examples: + + >>> from brial.frontend import * + >>> r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) + >>> x0>x1 + True + >>> x0>x1*x2 + True + >>> y0>y1 + True + >>> y0>y1*y2 + True + + >>> r = r.clone(ordering=dlex) + >>> r(x0) > r(x1) + True + >>> r(x0) > r(x1*x2) + False + + >>> r = r.clone(ordering=dp_asc) + >>> r(x0) > r(x1) + False + >>> r(x0) > r(x1*x2) + False + + >>> r = r.clone(ordering=block_dlex, blocks=[3]) + >>> r(x0) > r(x1) + True + >>> r(x0) > r(x1*x2) + False + >>> r(x0) > r(y0*y1*y2) + True + + >>> r = r.clone(ordering=block_dp_asc) + >>> r(x0) > r(x1) + False + >>> r(x0) > r(y0) + False + >>> r(x0) > r(x1*x2) + False + + >>> r = r.clone(ordering=block_dp_asc, blocks=[3]) + >>> r(x0) > r(y0) + True + + >>> r(x0) > r(y0*y1) + True + + >>> r = r.clone(names=["z17", "z7"]) + >>> [r.variable(idx) for idx in xrange(3)] + [z17, z7, x2] + >>> r = r.clone(names="abcde") + >>> [r.variable(idx) for idx in xrange(6)] + [a, b, c, d, e, y2] + +""" + +from sage import all +from sage.rings.polynomial.pbori.pbori import * + +import weakref + + +OrderCode = type('OrderCode', (object,), order_dict) + + +def Ring(n, order='lp', names=None, blocks=[]): + + pbnames = names + if pbnames is None: + pbnames = ['x(' + str(idx) + ')' for idx in range(n)] + order = TermOrder_from_pb_order(n, order, blocks) + R = BooleanPolynomialRing(n, names=pbnames, order=order) + return R + +BoolePolynomialVector = BooleanPolynomialVector + + +#todo: PolyBoRi's original interface uses its WeakRingPtr here +def WeakRingRef(ring): + return weakref.weakref(ring) + +Monomial = MonomialFactory() +Polynomial = PolynomialFactory() +Variable = VariableFactory() + + +_add_up_polynomials = add_up_polynomials + + +def add_up_polynomials(polys, init): + """ + Adds up the polynomials in polys (which should be a BoolePolynomialVector or a sequence of ??? + """ + if not isinstance(polys, BoolePolynomialVector): + vec = BoolePolynomialVector + for p in polys: + vec.append(p) + polys = vec + + return _add_up_polynomials(polys, init) + +_gauss_on_polys = gauss_on_polys + + +def gauss_on_polys(l): + vec = BoolePolynomialVector(l) + return list(_gauss_on_polys(vec)) diff --git a/src/sage/rings/polynomial/pbori/__init__.py b/src/sage/rings/polynomial/pbori/__init__.py new file mode 100644 index 00000000000..6a792e2cd27 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/__init__.py @@ -0,0 +1,45 @@ +"""The PolyBoRi package implements a framework for computations with Polynomials in Boolean Ring. + +The core of PolyBoRi is a C++ library, which provides high-level data types for Boolean polynomials and monomials, +exponent vectors, as well as for the underlying polynomial rings and subsets of the powerset of the Boolean variables. +The description of the latter can be found in the description of the 'dynamic' submodule, as well as in the doxygen-based documentation. + +As a unique approach, binary decision diagrams are used as internal storage type for polynomial structures. +On top of this C++-library we provide a Python interface. This allows parsing of complex polynomial systems, +as well as sophisticated and extendable strategies for Groebner base computation. +PolyBoRi features a powerful reference implementation for Groebner basis computation. + +AUTHOR: + The PolyBoRi Team, 2007-2011 + +REFERENCES: +M. Brickenstein, A. Dreyer, G. Greuel, M. Wedler, O. Wienand, +New developments in the theory of Groebner bases and applications +to formal Verification, Preprint at http://arxiv.org/abs/0801.1177 + +M. Brickenstein, A. Dreyer, PolyBoRi: +A Groebner Basis Framework for Boolean Polynomials, +Reports of Fraunhofer ITWM, No. 122, Kaiserslautern, Germany, 2007. +http://www.itwm.fraunhofer.de/zentral/download/berichte/bericht122.pdf + +M. Brickenstein, A. Dreyer, PolyBoRi: +A framework for Groebner basis computations with Boolean polynomials, +Electronic Proceedings of the MEGA 2007 - Effective Methods in Algebraic Geometry, Strobl, Austria, June 2007. +http://www.ricam.oeaw.ac.at/mega2007/electronic/electronic.html +""" + +from .PyPolyBoRi import * + +# Get all-inclusive groebner routine +from .gbcore import groebner_basis +from .nf import normal_form + +# Import some high-level modelling functionality +from .blocks import declare_ring +from .blocks import HigherOrderBlock, AlternatingBlock, Block +from .gbrefs import load_file +from .specialsets import * + + +def plist(a, b): + return [a, b] diff --git a/src/sage/rings/polynomial/pbori/addition.py b/src/sage/rings/polynomial/pbori/addition.py new file mode 100644 index 00000000000..2edae791592 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/addition.py @@ -0,0 +1,144 @@ +from .PyPolyBoRi import Polynomial, BooleSet, BooleConstant +from .partial import PartialFunction +from .specialsets import all_monomials_of_degree_d, power_set +from .ll import ll_encode, ll_red_nf_redsb + + +def add_bits_old(bits): + """Adds n bits + >>> from brial import * + >>> r=Ring(10) + >>> add_bits_old([r.variable(i) for i in xrange(3)]) + [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] + >>> add_bits_old([r.variable(i) for i in xrange(4)]) + [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3)] + """ + bits = list(bits) + n = len(bits) + deg_d_monomials = [Polynomial(all_monomials_of_degree_d(i, bits)) for i in + range(n + 1)] + full = power_set(bits) + bits_expr = [] # [sum(bits)] + step = 0 + while n > 2 ** step: + to_one = sum([deg_d_monomials[i] for i in range(n + 1) if i & 2 ** + step]) + to_one = Polynomial(to_one) + fun = PartialFunction(ones=to_one, zeros=full.diff(to_one)) + poly = fun.interpolate_smallest_lex() + bits_expr.append(poly) + step = step + 1 + return bits_expr + + +def add_bits(bits): + """Adds n bit variables, by Lucas theorem + >>> from brial import * + >>> r=Ring(10) + >>> add_bits([r.variable(i) for i in xrange(3)]) + [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] + >>> add_bits([r.variable(i) for i in xrange(4)]) + [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3), x(0)*x(1)*x(2)*x(3)] + >>> add_bits([r.variable(0)]) + [x(0)] + """ + bits = list(bits) + if len(bits) < 2: + return bits + n = len(bits) + + bits_expr = [] # [sum(bits)] + step = 0 + while n >= 2 ** step: + bits_expr.append(Polynomial(all_monomials_of_degree_d(2 ** step, bits) + )) + step = step + 1 + return bits_expr + + +def add_bit_expressions(bit_expressions): + """Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. + + >>> from brial import * + >>> r=Ring(20) + >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) + [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] + >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) + [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] + >>> add_bit_expressions([r.variable(11), r.variable(11)]) + [0, x(11)] + >>> add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) + [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] + """ + + bit_variables = [] + if bit_expressions: + ring = bit_expressions[0].ring() + bit_variables = [ring.variable(i) for i in range(len(bit_expressions) + )] + for expr in bit_expressions: + assert BooleSet(expr).navigation().value() >= len(bit_variables) + mapping = ll_encode([b + expr for (b, expr) in zip(bit_variables, + bit_expressions)]) + return [ll_red_nf_redsb(p, mapping) for p in add_bits(bit_variables)] + + +def add_words(words): + """def adds n words, this words are supposed to consists of list of their bits. + >>> from brial import * + >>> r=Ring(1000) + >>> add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) + [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] + >>> res=add_words([[r.variable(100+i*9+j) for i in xrange(4)] for j in xrange(9)]) + >>> [len(p) for p in res] + [9, 45, 495, 12870, 735462, 70285482, 1891358892, 6435] + >>> [p.deg() for p in res] + [1, 2, 4, 8, 12, 18, 25, 33] + >>> [p.n_nodes() for p in res] + [9, 25, 54, 100, 153, 211, 249, 100] + """ + + max_word_length = max((len(w) for w in words)) + res = [] + while len(words) > 0: + words = [w for w in words if len(w) > 0] + bits = add_bit_expressions([w[0] for w in words]) + words = [w[1:] for w in words] + if len(bits) > 0: + res.append(bits[0]) + words.append(bits[1:]) + return res + + +def multiply_by_addition(word_a, word_b): + """Multiply two words + >>> from brial import Ring + >>> r=Ring(1000) + >>> x = r.variable + >>> n=7 + >>> res=multiply_by_addition([x(200+2*i) for i in xrange(n)], [x(200+2*i+1) for i in xrange(n)]) + >>> [p.n_nodes() for p in res] + [2, 4, 7, 17, 38, 85, 222, 630, 1358, 1702, 1713, 1430, 875, 214, 0] + """ + word_a = list(word_a) + word_b = list(word_b) + summands = [] + if word_a: + zero = word_a[0].ring().zero() + elif word_b: + zero = word_b[0].ring().zero() + else: + zero = BooleConstant(0) + + for (i, a) in enumerate(word_a): + summands.append(i * [zero] + [a * b for b in word_b]) + + return add_words(summands) + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py new file mode 100644 index 00000000000..2152c33b35b --- /dev/null +++ b/src/sage/rings/polynomial/pbori/blocks.py @@ -0,0 +1,453 @@ +from __future__ import print_function + +import sys +if __name__ == '__main__': + import pathadjuster + +from .PyPolyBoRi import Ring, VariableBlock, Polynomial +from .PyPolyBoRi import VariableFactory, MonomialFactory +from itertools import chain, islice +#class BlockEndException(object): + #pass + #def __init__(self, arg): + # self.arg = arg + # pass + + +class Block(object): + """The block class represents a block of variables + (start_index,...,start_index+size-1), it is the preferred + block type for simple one-dimensional variable sets""" + + def __init__(self, var_name, size, start_index=0, reverse=False): + indices = range(start_index, start_index + size) + if reverse: + indices = reversed(indices) + #self.index2pos=dict([(v,k) for (k,v) in enumerate(indices)]) + self.names = [var_name + "(" + str(i) + ")" for i in indices] + self.var_name = var_name + self.start_index = start_index + self.reverse = reverse + self.size = size + + def __iter__(self): + return iter(self.names) + + def __getitem__(self, i): + return self.names[i] + + def __len__(self): + return self.size + + def register(self, start, context): + #def var_func(i): + # return Variable(self.index2pos[i]+start) + ring_context = context + while isinstance(ring_context, PrefixedDictProxy): + ring_context = ring_context.wrapped + ring = ring_context['r'] + + var_func = VariableBlock(self.size, self.start_index, start, self. + reverse, ring) + var_func.__name__ = self.var_name + context[self.var_name] = var_func + + +class AlternatingBlock(object): + """The Alternating Block class is used for doing tricky variable + schemes,where base names vary, e.g. + a(0),b(0),a(1),b(1),a(2),b(2)""" + + def __init__(self, var_names, size_per_variable, start_index=0, + reverse=False): + self.var_names = var_names + self.size_per_variable = size_per_variable + self.reverse = reverse + indices = range(start_index, start_index + size_per_variable) + + if reverse: + indices = reversed(indices) + names = [] + for i in indices: + for n in var_names: + names.append(n + "(" + str(i) + ")") + self.indices = indices + self.index2pos = dict([(v, k) for (k, v) in enumerate(indices)]) + self.names = names + + def __len__(self): + return self.size_per_variable * len(self.var_names) + + def __iter__(self): + return iter(self.names) + + def __getitem__(self, i): + return self.names[i] + + def register(self, start, context): + def gen_var_func(var_pos): + + class var_factory(object): + def __init__(self, ring, index2pos, size): + self.ring = ring + self.index2pos = index2pos + self.size = size + + def __call__(self, idx): + return self.ring.variable(self.index2pos[idx] * self.size + + var_pos + start) + ring_context = context + while isinstance(ring_context, PrefixedDictProxy): + ring_context = ring_context.wrapped + ring = ring_context['r'] + + return var_factory(ring, self.index2pos, len(self.var_names)) + + for (var_pos, n) in enumerate(self.var_names): + var_func = gen_var_func(var_pos) + var_func.__name__ = n + context[n] = var_func + + +def shift(f, i): + def g(j): + return f(i + j) + g.__name__ = f.__name__ + return g + + +class AdderBlock(AlternatingBlock): + def __init__(self, adder_bits, sums="s", carries="c", input1="a", + input2="b", start_index=0): + AlternatingBlock.__init__(self, (sums, carries, input1, input2), + adder_bits, start_index=start_index, reverse=True) + self.input1 = input1 + self.input2 = input2 + self.sums = sums + self.carries = carries + self.start_index = start_index + self.adder_bits = adder_bits + + def register(self, start, context): + super(AdderBlock, self).register(start, context) + a = context[self.input1] + b = context[self.input2] + self.s = shift(context[self.sums], self.start_index) + self.c = shift(context[self.carries], self.start_index) + a = shift(a, self.start_index) + b = shift(b, self.start_index) + carries = [Polynomial(a(0).ring().zero())] + for i in range(self.adder_bits): + #print i, ":" + c = 1 + (1 + a(i) * b(i)) * (1 + carries[-1] * a(i)) * (1 + + carries[-1] * b(i)) + carries.append(c) + + self.add_results = [a(i) + b(i) + carries[i] for i in range(self. + adder_bits)] + self.carries_polys = carries[1:] + + #def s(i): + # return self.add_results[i-self.start_index] + #def c(i): + # return self.carries_polys[i-self.start_index] + #context[self.sums]=s + #context[self.carries]=c + def implement(self, equations): + for i in range(self.adder_bits): + equations.append(self.s(i) + self.add_results[i]) + equations.append(self.c(i) + self.carries_polys[i]) + pass + + +class HigherOrderBlock(object): + """HigherOrderBlocks are multidimensional blocks of variables, for each dimension a seperate start_index and size can be specified + + var_name : variables will be called (multiindex), where multiindex is a tuple of the size + size_tuple : specifies the sizes of the ranges of each component of the multi-indices + start_index_tuple : the multi-indices will be of the form start_index_tuple + a, where a is a multi-index with non-negative components + + """ + + def __init__(self, var_name, size_tuple, start_index_tuple=None, + reverse=False): + if start_index_tuple is None: + start_index_tuple = len(size_tuple) * (0, ) + cart = [()] + assert len(size_tuple) == len(start_index_tuple) + outer_indices = reversed(range(len(size_tuple))) + for i in outer_indices: + s_i = start_index_tuple[i] + s = size_tuple[i] + #print "cart", cart + cart = [(j, ) + c for j in range(s_i, s_i + s) for c in cart] + if reverse: + cart.reverse() + self.cart = cart + self.cart2index = dict([(v, k) for (k, v) in enumerate(cart)]) + self.var_name = var_name + self.names = [var_name + str(c) for c in cart] + pass + + def __getitem__(self, i): + return self.names[i] + + def __iter__(self): + return iter(self.names) + + def __len__(self): + return len(self.names) + + def register(self, start, context): + def var_func(*indices): + return Variable(self.cart2index[indices] + start) + var_func.__name__ = self.var_name + context[self.var_name] = var_func + + +class InOutBlock(object): + def __init__(self, out_size, in_size, output="out", input="in", + in_start_index=0, out_start_index=0, + out_reverse=False, in_reverse=False): + self.output = Block(var_name=output, start_index=out_start_index, + size=out_size, reverse=out_reverse) + self.input = Block(var_name=input, start_index=in_start_index, + size=in_size, reverse=in_reverse) + self.out_start_index = out_start_index + + self.in_start_index = in_start_index + + def __iter__(self): + return chain(self.output, self.input) + + def __getitem__(self, i): + if (i < len(self.output)): + return self.output[i] + else: + return self.input[i - len(self.output)] + + def __len__(self): + return len(self.output) + len(self.input) + + def register(self, start, context): + self.output.register(start, context) + self.input.register(start + len(self.output), context) + self.out_vars = shift(context[self.output.var_name], self. + out_start_index) + self.in_vars = shift(context[self.input.var_name], self.in_start_index) + pass + + +class MultiBlock(object): + def __init__(self, sizes=[], var_names=["v"], start_indices=[], reverses=[ + ]): + + self.start_indices = start_indices + [0] * (len(var_names) - len( + start_indices)) + reverses += [False] * (len(var_names) - len(reverses)) + sizes += [1] * (len(var_names) - len(sizes)) + + self.blocks = [Block(var_name=var_names[idx], size=sizes[idx], + start_index=self.start_indices[idx], reverse=reverses[idx]) for + idx in range(len(var_names))] + + def __iter__(self): + return chain(*self.blocks) + + def __getitem__(self, i): + return next(islice(chain(*self.blocks), i, i + 1)) + # sum([bl.names for bl in self.blocks])[i] + + def __len__(self): + return sum((len(bl) for bl in self.blocks)) + + def register(self, start, context): + offset = 0 + for bl in self.blocks: + bl.register(start + offset, context) + offset += len(bl) + + self.vars = [shift(context[self.blocks[idx].var_name], self. + start_indices[idx]) for idx in range(len(self.blocks))] + + +class PrefixedDictProxy(object): + """docstring for PrefixedDictProxy""" + + def __init__(self, wrapped, prefix): + super(PrefixedDictProxy, self).__init__() + self.wrapped = wrapped + self.prefix = prefix + + def __getitem__(self, k): + try: + return self.wrapped[self.prefix + k] + except KeyError: + print(self.prefix, k, list(self.wrapped)) + raise KeyError + + def __setitem__(self, k, v): + self.wrapped[self.prefix + k] = v + + +class MacroBlock(object): + def __init__(self, prefix): + + self.prefix = prefix + self.blocks = [] + self.combinations = [] + self.connections = [] + + def declare(self, blocks): + self.blocks = blocks + + def connect(self, combinations): + self.combinations = combinations + + def __iter__(self): + return (self.prefix + "_" + n for n in chain(*self.blocks)) + + def __getitem__(self, i): + return self.prefix + "_" + next(islice(chain(*self.blocks), i, i + 1)) + #for bl in self.blocks: + # if i >= len(bl): + # i -= len(bl) + # else: + # return bl[i] + + def __len__(self): + return sum((len(bl) for bl in self.blocks)) + + def resolve(self, localname): + return self.prefix + "_" + localname + + def register(self, start, context): + context = PrefixedDictProxy(context, self.prefix + "_") + offset = 0 + for bl in self.blocks: + bl.register(start + offset, context) + offset += len(bl) + + for ((con1, indices1), (con2, indices2)) in self.combinations: + for idx in range(min(len(indices1), len(indices2))): + self.connections += [context[con1](indices1[idx]) + context[ + con2](indices2[idx])] + + def implement(self, equations): + for bl in self.blocks: + if hasattr(bl, "implement"): + bl.implement(equations) + + equations += self.connections + + +class IfThen(object): + def __init__(self, ifpart, thenpart, supposed_to_be_valid=True): + self.ifpart = [Polynomial(p) for p in ifpart] + self.thenpart = [Polynomial(p) for p in thenpart] + self.supposedToBeValid = supposed_to_be_valid + + def __str__(self): + return ("If(AND(" + ", ".join([str(p) + " == 0" for p in self.ifpart]) + + ")), THEN " + ", ".join([str(p) + " == 0" for p in self.thenpart + ])) + + +def if_then(i, t, supposed_to_be_valid=True): + return IfThen(i, t, supposed_to_be_valid) + + +def declare_ring(blocks, context=None): + """Declare Ring is the preferred function to create a ring and declare a variable scheme, + the number of variables is automatically determined, + usually you pass globals() as context argument to store the ring and the variable mapping. + Example + declare_ring([Block("x",10),Block("y",5)],globals()) + gives a ring with x(0..9),y(0..4) and registers the ring as r, + and the variable blocks x and y in the context dictionary globals(), which consists of the global variables of the python module + """ + if context is None: + context = sys.modules['__main__'].__dict__ + + def canonicalize(blocks): + for elt in blocks: + if isinstance(elt, str): + yield elt + else: + for subelt in elt: + yield subelt + + blocks = list(blocks) + n = 0 + + for b in blocks: + if isinstance(b, str): + n = n + 1 + else: + n = n + len(b) + + r = Ring(n, names=canonicalize(blocks)) + + context["internalVariable"] = VariableFactory(r) +# context["Monomial"] = MonomialFactory(r) + context["r"] = r + declare_block_scheme(blocks, context) + return r + + +def declare_block_scheme(blocks, context): + start = 0 + block_starts = [] + ring = context["r"] + for b in blocks: + if start != 0: + block_starts.append(start) + if isinstance(b, str): + context[b] = context["internalVariable"](start) + #_cpp_set_variable_name(ring, start, b) + start = start + 1 + else: + b.register(start, context) + #for (pos,name) in enumerate(b): + # _cpp_set_variable_name(ring, start+pos, name) + start = start + len(b) + context["block_start_hints"] = block_starts + context["number_of_declared_vars"] = start + + +def main(): + r = Ring(1000) + ablock = AlternatingBlock(["a", "b", "c"], 100) + declare_block_scheme([ablock], globals()) + for i in range(10): + print(r.variable(i)) + + print(list(ablock)) + declare_block_scheme([ + Block(var_name="x", size=100), + HigherOrderBlock("y", (3, 4, 11, 2)), + AlternatingBlock(["a", "b", "c"], 100)], + globals()) + for i in range(10): + print(x(i)) + print(y(0, 0, 0, 0)) + print(y(0, 0, 0, 1)) + print(y(0, 0, 1, 0)) + print(y(0, 0, 1, 1)) + print(a(0), a(1), a(2), b(0), b(1), c(0)) + declare_block_scheme([ + Block(var_name="x", size=100, reverse=True), + HigherOrderBlock("y", (3, 4, 11, 2), reverse=True), + AlternatingBlock(["a", "b", "c"], 100, reverse=True)], + globals()) + for i in range(10): + print(x(i)) + print(y(0, 0, 0, 0)) + print(y(0, 0, 0, 1)) + print(y(0, 0, 1, 0)) + print(y(0, 0, 1, 1)) + print(a(0), a(1), a(2), b(0), b(1), c(0)) + declare_block_scheme(["a", "b", "c"], globals()) + print(a, b, c) +if __name__ == '__main__': + main() diff --git a/src/sage/rings/polynomial/pbori/check_claims.py b/src/sage/rings/polynomial/pbori/check_claims.py new file mode 100644 index 00000000000..66529ab0c85 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/check_claims.py @@ -0,0 +1,205 @@ +# encoding: utf-8 +""" +untitled.py + +Created by Michael Brickenstein on 2007-03-05. +Copyright (c) 2007 The PolyBoRi Team. See LICENSE file. +""" +from __future__ import print_function + +import sys +from optparse import OptionParser +#if __name__ == "__main__": +# import pathadjuster +from .PyPolyBoRi import Polynomial, Monomial, BooleConstant, BooleSet +from .PyPolyBoRi import recursively_insert +from .gbrefs import my_import, load_data, clean_data, load_file +from .blocks import IfThen +from copy import copy +from .ll import ll_encode, ll_red_nf_noredsb, ll_red_nf_redsb + + +def find_one(p, res=None): + def zero_nav(n): + return n.constant() and (not n.terminal_one()) + try: + p = p.navigation() + except AttributeError: + pass + if res is None: + res = dict() + if zero_nav(p): + raise ValueError + if p.terminal_one(): + return res + else_branch = p.else_branch() + if zero_nav(else_branch): + res[Monomial(Variable(p.value()))] = 1 + find_one(p.then_branch(), res) + else: + res[Monomial(Variable(p.value()))] = 0 + find_one(else_branch, res) + return res + +parser = OptionParser() +NF3 = "nf3" +LINEAR_LEAD_NOREDSB = "ll" +parser.add_option("--method", + action="store", dest="method", type="choice", + choices=["nf3", "linear-lead-redsb", LINEAR_LEAD_NOREDSB], + default="linear-lead-redsb", + help="select method") + + +def my_red_nf(p, strat): + if p.is_zero(): + return p + hr = nf3(strat.reduction_strategy, p, p.lead()) + if hr.is_zero(): + return hr + return red_tail(strat, hr) + + +def gen_strat(polys): + polys = [Polynomial(p) for p in polys] + polys = [p for p in polys if not p.is_zero()] + assert len(set([p.lead() for p in polys])) == len(polys) + assert polys + strat = GroebnerStrategy(polys[0].ring()) + for p in polys: + print("Adding") + strat.add_generator(p) + print("finished") + return strat + + +def logicaland(l): + res = BooleConstant(0) + for p in l: + res = 1 + (res + 1) * (p + 1) + return res + + +def logicalor(l): + res = BooleConstant(1) + for p in l: + res = res * p + return res + + +def proof(ifthen, strat): + ip = ifthen.ifpart + it = ifthen.thenpart + print("proofing:", ifthen) + c = logicalor([1 + logicaland(ip), logicaland(it)]) + if c.is_zero(): + print("TRUE (trivial)") + return + else: + c = nf3(strat.reduction_strategy, c, c.lead()) + if c.is_zero(): + print("TRUE") + return + else: + print("FALSE") + + +def proofll(ifthen, reductors, redsb=True, prot=True): + + if prot and (not ifthen.supposedToBeValid): + print("THIS THEOREM IS NOT SUPPOSED TO BE VALID") + ip_pre = ifthen.ifpart + ip = [] + + for p in ip_pre: + p = Polynomial(p) + if p.is_zero(): + continue + li = list(p.lead().variables()) + if len(li) == 1 and (not (li[0] in list(Polynomial(reductors).lead(). + variables()))): + assert not Polynomial(reductors).is_zero() + lead_index = li[0] + if redsb: + p = ll_red_nf_redsb(p, reductors) + reductors = ll_red_nf_redsb(Polynomial(reductors), BooleSet(p. + set())) + + p_nav = p.navigation() + reductors = recursively_insert(p_nav.else_branch(), p_nav.value(), + reductors) + else: + ip.append(p) + it = ifthen.thenpart + if prot: + print("proofing:", ifthen) + ip = logicaland(ip) + for c in it: + if prot: + print("proofing part:", c) + c = logicalor([BooleConstant(1) + ip, c]) + + if c.is_zero(): + if prot: + print("TRUE (trivial)") + return True + else: + c_orig = c + if redsb: + c = ll_red_nf_redsb(c, reductors) + else: + c = ll_red_nf_noredsb(c, reductors) + if c.is_zero(): + if prot: + print("TRUE") + return True + else: + if prot: + print("FAILED") + print("can construct COUNTER EXAMPLE with:", find_one(c)) + return False + + +def to_if_then(p): + if isinstance(p, IfThen): + return p + else: + return IfThen([], [p]) + + +def main(argv=None): + (opts, args) = parser.parse_args() + mydata = load_file(args[0]) + claims = mydata.claims + if opts.method == NF3: + strat = gen_strat(mydata.ideal) + for c in claims: + proof(to_if_then(c), strat) + del strat + try: + del c + except NameError: + pass + else: + if opts.method == LINEAR_LEAD_NOREDSB: + reductors = ll_encode(mydata.ideal) + for c in claims: + proofll(to_if_then(c), reductors, redsb=False) + del reductors + try: + del c + except NameError: + pass + else: + reductors = ll_encode(mydata.ideal, reduce=True) + for c in claims: + proofll(to_if_then(c), reductors) + del reductors + try: + del c + except NameError: + pass + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/sage/rings/polynomial/pbori/cluster.py b/src/sage/rings/polynomial/pbori/cluster.py new file mode 100644 index 00000000000..036f04dc42d --- /dev/null +++ b/src/sage/rings/polynomial/pbori/cluster.py @@ -0,0 +1,120 @@ +# -*- python -*- +# encoding: utf-8 +""" +cluster.py + +Created by Michael Brickenstein on 2011-08-05. +Copyright 2011 The PolyBoRi Team. See LICENSE file. +""" + +import sys +import os +from .statistics import used_vars, used_vars_set +from .PyPolyBoRi import Variable + + +def main(): + pass + + +class ClusterAlgorithmFailed(Exception): + pass + + +class ClusterAlgorithm(object): + def __init__(self, ideal, determination_modifier=1): + if len(ideal) == 0: + raise ValueError('ideal generators list should be non empty') + + self.ideal = ideal + + self.determination_modifier = determination_modifier + + self.used_variables_ideal = used_vars_set(ideal) + + self.number_of_used_variables_in_ideal = len(self.used_variables_ideal) + self.used_variables_of_polynomial = dict( + [(p, set(p.vars_as_monomial().variables())) for p in ideal]) + self.variables_introduction_mapping = dict() + + self.cluster = set() + self.used_variables_cluster = set() + self.build_variables_usage() + self.initialize_variables_introduction_mapping() + + def build_variables_usage(self): + self.variables_usage = dict() + for (p, variables) in self.used_variables_of_polynomial.items(): + for v in variables: + self.variables_usage.setdefault(v, []).append(p) + + def initialize_variables_introduction_mapping(self): + self._build_variables_introduction_mapping() + + def _build_variables_introduction_mapping(self): + def var_set_to_tuple(s): + return tuple(sorted(s, key=Variable.index)) + self.variables_introduction_mapping.clear() + for (p, var_set) in self.used_variables_of_polynomial.items(): + if p in self.cluster: + continue + as_tuple = var_set_to_tuple(var_set.difference(self. + used_variables_cluster)) + self.variables_introduction_mapping.setdefault( + as_tuple, []).append(p) + + def adjust_variables_introduction_mapping(self, introduced_variables): + self._build_variables_introduction_mapping() + + def determined_enough(self): + return (self.number_of_used_variables_in_cluster + self. + determination_modifier <= len(self.cluster)) + + def find_cluster(self): + p = self.initial_choice() + self.cluster = set() + self.add_polynomial_to_cluster(p) + while not self.determined_enough(): + self.increase_cluster() + return list(self.cluster) + + def add_polynomial_to_cluster(self, p): + self.cluster.add(p) + self.used_variables_cluster = set(used_vars_set(self.cluster). + variables()) + self.number_of_used_variables_in_cluster = len(self. + used_variables_cluster) + self.adjust_variables_introduction_mapping(self. + used_variables_of_polynomial[p]) + + def initial_choice(self): + def max_key(entry): + (entry_variable, entry_polynomials) = entry + return len(entry_polynomials) + (variable, polynomials) = max(self.variables_usage.items(), + key=max_key) + + def min_key(p): + return len(self.used_variables_of_polynomial[p]) + return min(polynomials, key=min_key) + + def increase_cluster(self): + introduced_variables_possibilities = (list(self. + variables_introduction_mapping.keys())) + introduced_variables = min(introduced_variables_possibilities, key=len) + polynomials = self.variables_introduction_mapping[introduced_variables] + assert len(polynomials) > 0 + for p in polynomials: + self.add_polynomial_to_cluster(p) + if len(self.cluster) == len(self.ideal): + raise ClusterAlgorithmFailed + self.adjust_variables_introduction_mapping(introduced_variables) + + +def find_cluster(ideal): + algorithm = ClusterAlgorithm(ideal) + return algorithm.find_cluster() + + +if __name__ == '__main__': + main() diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py new file mode 100644 index 00000000000..4064c598054 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/cnf.py @@ -0,0 +1,241 @@ +from random import Random +from .PyPolyBoRi import (Monomial, BooleSet, Polynomial, if_then_else + as ite, lp, gauss_on_polys, ll_red_nf_redsb) +from .ll import ll_encode +from .statistics import used_vars_set + + +class CNFEncoder(object): + def __init__(self, r, random_seed=16): + self.random_generator = Random(random_seed) + self.one_set = r.one().set() + self.empty_set = r.zero().set() + self.r = r + + def zero_blocks(self, f): + """divides the zero set of f into blocks + >>> from brial import * + >>> r = declare_ring(["x", "y", "z"], dict()) + >>> e = CNFEncoder(r) + >>> e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) + [{y: 0}, {z: 0}, {x: 0}] + """ + f = Polynomial(f) + variables = f.vars_as_monomial() + + space = variables.divisors() + variables = list(variables.variables()) + zeros = f.zeros_in(space) + rest = zeros + res = list() + + def choose_old(s): + return next(iter(rest)) # somewhat + + #inefficient compared to polynomials lex_lead + def choose(s): + indices = [] + assert not s.empty() + nav = s.navigation() + while not nav.constant(): + e = nav.else_branch() + t = nav.then_branch() + if e.constant() and not e.terminal_one(): + indices.append(nav.value()) + nav = t + else: + if self.random_generator.randint(0, 1): + indices.append(nav.value()) + nav = t + + else: + nav = e + assert nav.terminal_one() + res = self.one_set + for i in reversed(indices): + res = ite(i, res, self.empty_set) + return next(iter(res)) + while not rest.empty(): + l = choose(rest) + l_variables = set(l.variables()) + + def get_val(var): + if var in l_variables: + return 1 + return 0 + block_dict = dict([(v, get_val(v)) for v in variables]) + + l = l.set() + self.random_generator.shuffle(variables) + for v in variables: + candidate = l.change(v.index()) + if candidate.diff(zeros).empty(): + l = l.union(candidate) + del block_dict[v] + rest = rest.diff(l) + res.append(block_dict) + return res + + def clauses(self, f): + """ + >>> from brial import * + >>> r = declare_ring(["x", "y", "z"], dict()) + >>> e = CNFEncoder(r) + >>> e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS + [{...x: 0...}] + >>> e.clauses(r.variable(1)+r.variable(0)) # doctest:+ELLIPSIS + [{...x: 1...}, {...y: 1...}] + + >>> [sorted(c.iteritems()) for c in e.clauses(r.variable(0)*r.variable(1)*r.variable(2))] + [[(z, 0), (y, 0), (x, 0)]] + >>> [sorted(c.iteritems()) for c in e.clauses(r.variable(1)+r.variable(0))] + [[(y, 1), (x, 0)], [(y, 0), (x, 1)]] + """ + f_plus_one = f + 1 + blocks = self.zero_blocks(f + 1) + negated_blocks = [dict([(variable, 1 - value) for (variable, value) + in b.items()]) for b in blocks] + # we form an expression for a var configuration *not* lying in the + # block it is evaluated to 0 by f, iff it is not lying in any zero + # block of f+1 + return negated_blocks + + def polynomial_clauses(self, f): + """ + >>> from brial import * + >>> r = declare_ring(["x", "y", "z"], dict()) + >>> e = CNFEncoder(r) + >>> e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) + [x*y*z] + >>> v = r.variable + >>> p = v(1)*v(2)+v(2)*v(0)+1 + >>> groebner_basis([p], heuristic = False)==groebner_basis(e.polynomial_clauses(p), heuristic = False) + True + """ + + def product(l): + res = l[0] + for p in l[1:]: + res = res * p + # please care about the order of these multiplications for + # performance + return res + return [product([variable + value for (variable, value) + in b.items()]) for b in self.clauses(f)] + + def to_dimacs_index(self, v): + return v.index() + 1 + + def dimacs_encode_clause(self, c): + def get_sign(val): + if value == 1: + return 1 + return -1 + + items = sorted(c.items(), reverse=True) + return " ".join( + [str(v) for v in + [ + get_sign(value) * self.to_dimacs_index(variable) + for (variable, value) in items] + [0]]) + + def dimacs_encode_polynomial(self, p): + """ + >>> from brial import * + >>> d=dict() + >>> r = declare_ring(["x", "y", "z"], d) + >>> e = CNFEncoder(r) + >>> e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]) + ['1 2 -3 0', '1 -2 3 0', '-1 -2 -3 0', '-1 2 3 0'] + """ + clauses = self.clauses(p) + res = [] + for c in clauses: + res.append(self.dimacs_encode_clause(c)) + return res + + def dimacs_cnf(self, polynomial_system): + r""" + >>> from brial import * + >>> r = declare_ring(["x", "y", "z"], dict()) + >>> e = CNFEncoder(r) + >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) + 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0' + >>> e.dimacs_cnf([r.variable(1)+r.variable(0)]) + 'c cnf generated by PolyBoRi\np cnf 3 2\n1 -2 0\n-1 2 0' + >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) + 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n-1 2 0\n1 -2 0' + """ + clauses_list = [c for p in polynomial_system for c in self. + dimacs_encode_polynomial(p)] + res = ["c cnf generated by PolyBoRi"] + r = polynomial_system[0].ring() + n_variables = r.n_variables() + res.append("p cnf %s %s" % (n_variables, len(clauses_list))) + for c in clauses_list: + res.append(c) + return "\n".join(res) + + +class CryptoMiniSatEncoder(CNFEncoder): + group_counter = 0 + + def dimacs_encode_polynomial(self, p): + r""" + >>> from brial import * + >>> d=dict() + >>> r = declare_ring(["x", "y", "z"], d) + >>> e = CryptoMiniSatEncoder(r) + >>> p = d["x"]+d["y"]+d["z"] + >>> p.deg() + 1 + >>> len(p) + 3 + >>> e.dimacs_encode_polynomial(p) + ['x1 2 3 0\nc g 1 x + y + z'] + >>> e.dimacs_encode_polynomial(p+1) + ['x1 2 -3 0\nc g 2 x + y + z + 1'] + """ + if p.deg() != 1 or len(p) <= 1: + res = super(CryptoMiniSatEncoder, self).dimacs_encode_polynomial(p) + else: + + if p.has_constant_part(): + invert_last = True + else: + invert_last = False + variables = list(p.vars_as_monomial().variables()) + indices = [self.to_dimacs_index(v) for v in variables] + if invert_last: + indices[-1] = -indices[-1] + indices.append(0) + res = ["x" + " ".join([str(v) for v in indices])] + self.group_counter = self.group_counter + 1 + group_comment = "\nc g %s %s" % (self.group_counter, str(p)[:30]) + return [c + group_comment for c in res] + + def dimacs_cnf(self, polynomial_system): + r""" + >>> from brial import * + >>> r = declare_ring(["x", "y", "z"], dict()) + >>> e = CryptoMiniSatEncoder(r) + >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) + 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0\nc g 1 x*y*z\nc v 1 x\nc v 2 y\nc v 3 z' + >>> e.dimacs_cnf([r.variable(1)+r.variable(0)]) + 'c cnf generated by PolyBoRi\np cnf 3 1\nx1 2 0\nc g 2 x + y\nc v 1 x\nc v 2 y' + >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) + 'c cnf generated by PolyBoRi\np cnf 3 2\n-1 -2 -3 0\nc g 3 x*y*z\nx1 2 0\nc g 4 x + y\nc v 1 x\nc v 2 y\nc v 3 z' + """ + uv = list(used_vars_set(polynomial_system).variables()) + res = super(CryptoMiniSatEncoder, self).dimacs_cnf(polynomial_system) + res = res + "\n" + "\n".join(["c v %s %s" % (self.to_dimacs_index(v), + v) for v in uv]) + return res + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/coding.py b/src/sage/rings/polynomial/pbori/coding.py new file mode 100644 index 00000000000..bae1049a323 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/coding.py @@ -0,0 +1,91 @@ +from __future__ import print_function + +from .PyPolyBoRi import Polynomial +from .nf import symmGB_F2_C +from .ll import ll_encode + +try: + from itertools import ifilter as filter +except ImportError: + pass + + +class OccCounter(object): + def __init__(self): + self.impl = dict() + + def __getitem__(self, k): + try: + return self.impl[k] + except KeyError: + return 0 + + def increase(self, k): + try: + self.impl[k] = self.impl[k] + 1 + except KeyError: + self.impl[k] = 1 + + def uniques(self): + def filter_fun(k): + return self.impl[k] == 1 + return filter(filter_fun, self.impl.keys()) + + +def preprocess(I, prot=True): + def min_gb(I): + strat = symmGB_F2_C(I, opt_lazy=False, opt_exchange=False, prot=prot, + selection_size=10000, opt_red_tail=True) + return list(strat.minimalize_and_tail_reduce()) + I = [Polynomial(p) for p in I] + lin = [p for p in I if p.deg() == 1] + # lin_strat=symmGB_F2_C(lin, opt_lazy=False,opt_exchange=False,prot=prot,sele + # ction_size=10000,opt_red_tail=True) + lin = min_gb(lin) # list(lin_strat.minimalize_and_tail_reduce()) + for m in sorted([p.lead() for p in lin]): + print(m) + lin_ll = ll_encode(lin) + square = [p.lead() for p in I if p.deg() == 2 and len(p) == 1] + assert(len(lin) + len(square) == len(I)) + res = list(lin) + counter = OccCounter() + + def unique_index(s): + for idx in s: + if counter[idx] == 1: + return idx + never_come_here = False + assert never_come_here + + for m in square: + for idx in m: + counter.increase(idx) + systems = dict(((idx, []) for idx in counter.uniques())) + for m in square: + u_index = unique_index(m) + systems[u_index].append(ll_red_nf(m / Variable(u_index), lin_ll)) + rewritings = dict() + u_var = Variable(u) + u_im = ll_red_nf(u_var, lin_ll) + if not u_im.isConstant(): + if u_im != u_var: + r = u_im.navigation().value() + else: + pass + for u in counter.uniques(): + #print u + u_var = Variable(u) + u_im = ll_red_nf(u_var, lin_ll) + if not u_im.isConstant(): + if u_im != u_var: + r = u_im.navigation().value() + else: + pass + for u in systems.keys(): + u_var = Variable(u) + u_im = ll_red_nf(u_var, lin_ll) + res.extend([u_im * p for p in min_gb(systems[u])]) + #print [u_im*p for p in min_gb(systems[u])] + print("lin:", len(lin), "res:", len(res), "square:", len(square)) + res = [p for p in (Polynomial(p) for p in res) if not p.is_zero()] + return res diff --git a/src/sage/rings/polynomial/pbori/context.py b/src/sage/rings/polynomial/pbori/context.py new file mode 100644 index 00000000000..bdd1d8e05d0 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/context.py @@ -0,0 +1,101 @@ +if __name__ == '__main__': + from sys import path as search_path + from os import path as file_path + search_path.append(file_path.join(file_path.dirname(__file__), '..')) + + +def _exists(): + """PolyBoRi convention: checking optional components for prerequisites here + + >>> _exists() + True + """ + from distutils.sysconfig import get_python_version + return float(get_python_version()) > 2.4 + +from .PyPolyBoRi import Ring, VariableFactory, MonomialFactory +from .PyPolyBoRi import PolynomialFactory, SetFactory +from .PyPolyBoRi import Variable, Monomial, Polynomial, BooleSet +import polybori + + +class FactoryContext(object): + """Temporarily exchange the constructor of a given type with a compatible + callable object. It is useful together with the with statement. + + Example: + >>> r = Ring(1000) + >>> from brial import Variable + >>> def var(idx): return Variable(idx, r) + >>> with FactoryContext(Variable, var): + ... print Variable(17) + x(17) + >>> try: + ... print Variable(17) + ... except: + ... print "caught expected exception" + caught expected exception + """ + + def __init__(self, original, factory): + self.original = original + self.factory = factory + + def __enter__(self): + self.fallback = self.original.__init__ + + def func(orig, *args): + try: + self.fallback(orig, self.factory(*args)) + except: + self.fallback(orig, *args) + + self.original.__init__ = func + return self + + def __exit__(self, type, value, traceback): + self.original.__init__ = self.fallback + return False + + +class RingContext(object): + """Temporarily fix the ring for constructors of some ring-dependent types + like Variable and Monomial to a given ring. It is useful together with + the with statement. + + Example: + >>> r = Ring(1000) + >>> from brial import Variable + >>> print Variable(17, r) + x(17) + >>> with RingContext(r): + ... print Variable(17), Monomial(), Polynomial(0), BooleSet() + x(17) 1 0 {} + >>> try: + ... print Variable(17) + ... except: + ... print "caught expected exception" + caught expected exception + """ + + def __init__(self, ring): + self.contexts = (FactoryContext(Variable, VariableFactory(ring)), + FactoryContext(Monomial, MonomialFactory(ring)), + FactoryContext(Polynomial, PolynomialFactory(ring)), + FactoryContext(BooleSet, SetFactory(ring))) + + def __enter__(self): + for elt in self.contexts: + elt.__enter__() + return self + + def __exit__(self, type, value, traceback): + result = False + for elt in reversed(self.contexts): + result = result or elt.__exit__(type, value, traceback) + return result + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py new file mode 100644 index 00000000000..11e4793495c --- /dev/null +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -0,0 +1,49 @@ +from .interpolate import variety_lex_leading_terms, nf_lex_points +from .PyPolyBoRi import easy_linear_factors + + +def easy_linear_polynomials(p): + """ Get linear polynomials implied by given polynomial. + + >>> from brial.frontend import * + >>> easy_linear_polynomials(x(1)*x(2) + 1) + [x(1) + 1, x(2) + 1] + >>> easy_linear_polynomials(x(1)*x(2) + 0) + [] + >>> easy_linear_polynomials(x(0)*x(1) + x(0)*x(2) + 1) + [x(0) + 1, x(1) + x(2) + 1] + """ + res = [] + if p.deg() >= 2: + if p.vars_as_monomial().deg() > 8: + opp = p + 1 + for q in easy_linear_factors(opp): + res.append(q + 1) + else: + res = easy_linear_polynomials_via_interpolation(p) + return res + + +def easy_linear_polynomials_via_interpolation(p): + """ Get linear polynomials implied by given polynomial using interpolation + of the variety. + + >>> from brial.frontend import * + >>> easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) + [x(1) + 1, x(2) + 1] + >>> easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) + [] + >>> easy_linear_polynomials_via_interpolation(x(0)*x(1) + x(0)*x(2) + 1) + [x(0) + 1, x(1) + x(2) + 1] + """ + res = [] + p_vars = p.vars_as_monomial() + space = p_vars.divisors() + zeros = p.zeros_in(space) + lex_leads = variety_lex_leading_terms(zeros, p_vars) + for m in lex_leads: + if m.deg() == 1: + red = m + nf_lex_points(m, zeros) + if red.lead_deg() == 1: # normal ordering + res.append(red) + return res diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py new file mode 100644 index 00000000000..fa4e1d7d080 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -0,0 +1,80 @@ +if __name__ == "__main__": + import os + import sys + sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) + + + def _test(): + import doctest + doctest.testmod() + +from .PyPolyBoRi import BooleSet, Polynomial, BoolePolynomialVector, \ + FGLMStrategy, Monomial, Ring + +from .blocks import declare_ring + + +def _fglm(I, from_ring, to_ring): + """Unchecked variant of fglm""" + vec = BoolePolynomialVector(I) + return FGLMStrategy(from_ring, to_ring, vec).main() + + +def fglm(I, from_ring, to_ring): + """ + converts *reduced* Groebner Basis in from_ring to a GroebnerBasis in to_ring. + It acts independend of the global ring, which is restored at the end of the + computation, + >>> from brial.PyPolyBoRi import OrderCode + >>> dp_asc = OrderCode.dp_asc + >>> r=declare_ring(['x','y','z'],dict()) + >>> old_ring = r + >>> new_ring = old_ring.clone(ordering=dp_asc) + >>> (x,y,z) = [old_ring.variable(i) for i in xrange(3)] + >>> ideal=[x+z, y+z]# lp Groebner basis + >>> list(fglm(ideal, old_ring, new_ring)) + [y + x, z + x] + """ + for poly in I: + if poly.ring().id() != from_ring.id(): + raise ValueError("Ideal I must be from the first ring argument") + return _fglm(I, from_ring, to_ring) + + +def vars_real_divisors(monomial, monomial_set): + """ + returns all elements of of monomial_set, which result multiplied by a variable in monomial. + >>> from brial.PyPolyBoRi import OrderCode + >>> dp_asc = OrderCode.dp_asc + >>> from brial.PyPolyBoRi import Ring + >>> r=Ring(1000) + >>> x = r.variable + >>> b=BooleSet([x(1)*x(2),x(2)]) + >>> vars_real_divisors(x(1)*x(2)*x(3),b) + {{x(1),x(2)}} + """ + return BooleSet(Polynomial(monomial_set.divisors_of(monomial)). \ + graded_part(monomial.deg() - 1)) + + +def m_k_plus_one(completed_elements, variables): + """ calculates $m_{k+1}$ from the FGLM algorithm as described in Wichmanns diploma thesis + It would be nice to be able to efficiently extract the smallest term of a polynomial + >>> from brial.PyPolyBoRi import OrderCode + >>> dp_asc = OrderCode.dp_asc + >>> r=Ring(1000) + >>> x = r.variable + >>> s=BooleSet([x(1)*x(2),x(1),x(2),Monomial(r),x(3)]) + >>> variables=BooleSet([x(1),x(2),x(3)]) + >>> m_k_plus_one(s,variables) + x(2)*x(3) + >>> r2 = r.clone(ordering=dp_asc) + >>> m_k_plus_one(r2(s).set(),r2(variables).set()) + x(1)*x(3) + """ + return sorted(completed_elements.cartesian_product(variables).diff( + completed_elements))[0] + + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/frontend.py b/src/sage/rings/polynomial/pbori/frontend.py new file mode 100644 index 00000000000..4cfb8816a77 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/frontend.py @@ -0,0 +1,88 @@ +# Import basic functionality +r""" +This module defines an initial ring, and patches the declare_ring to use +a given context. + + +>>> x(0) +x(0) +>>> x(0)*x(0) +x(0) +>>> x(0) + x(0) +0 +>>> x(9999) +x(9999) +>>> x(9999)*x(9999) +x(9999) +>>> x(9999) + x(9999) +0 + +>>> from brial.frontend import * +>>> context = dict(globals()) +>>> polybori_start(context) # doctest: +ELLIPSIS +ipbori... +>>> r = context['declare_ring']('abc') +>>> context['a'] +a +>>> r.variable(0) +a +""" + +from __future__ import print_function + +from . import * +from .blocks import declare_ring as orig_declare_ring +from os import environ as env, path as os_path + + +def block_scheme_names(blocks): + """Helper for Singular interface.""" + + context = dict() + from .blocks import declare_block_scheme + declare_block_scheme(blocks, context) + + return list(context.keys()) + +ipbname = 'ipbori' + + +def polybori_copyright(): + print("""Copyright (c) 2007-2011 by The PolyBoRi Team. + Michael Brickenstein (MFO) brickenstein@mfo.de + Alexander Dreyer (ITWM) alexander.dreyer@itwm.fraunhofer.de + +The PolyBoRi Team is a joint project of + Mathematisches Forschungsinstitut Oberwolfach (MFO), Germany + Department of Mathematics, University of Kaiserslautern, Germany, and + Fraunhofer Institute for Industrial Mathematics (ITWM), Kaiserslautern, Germany. + +PolyBoRi incorporates the following works: + The CU Decision Diagrams Package Release 2.4.1 (CUDD) by Fabio Somenzi, + Copyright (c) 1995-2004, Regents of the University of Colorado. All Rights Reserved. + The M4RI Library - http://m4ri.sagemath.org + Copyright (C) 2007-2010, Martin Albrecht, Gregory Bard, and The M4RI Team""") + + +def polybori_license(): + print("""ipbori and the PolyBoRi framework are licensed under the terms of +the GNU General Public License (GPL) version 2 or later. +See http://www.gnu.org/licenses/ for details.""") + + +def polybori_start(global_context): + def declare_ring(blocks, context=None): + if context is None: + context = global_context + + return orig_declare_ring(blocks, context) + declare_ring.__doc__ = orig_declare_ring.__doc__ + global_context["declare_ring"] = declare_ring + + print(ipbname + """ -- The interactive command line tool of PolyBoRi %s +Type "polybori_copyright()" or "polybori_license()" for more information. +""" % global_context.get("polybori_version", '')) + +# Here come the defaults +r = Ring(10000) +x = VariableFactory(r) diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py new file mode 100644 index 00000000000..fd3c3d0c2e1 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -0,0 +1,676 @@ +from __future__ import print_function + +from .nf import * + +from .PyPolyBoRi import * +from .ll import eliminate, ll_encode +from time import time +from copy import copy +from itertools import chain +from inspect import getargspec +from .statistics import used_vars, used_vars_set +from .heuristics import dense_system, gauss_on_linear +from .easy_polynomials import easy_linear_polynomials +from itertools import chain +from .interpolate import lex_groebner_basis_for_polynomial_via_variety +from .fglm import _fglm + +try: + from inspect import getfullargspec as getargspec +except ImportError: + from inspect import getargspec + + +def get_options_from_function(f): + (argnames, varargs, varopts, defaults) = getargspec(f)[:4] + return dict( + zip( + argnames[-len(defaults):], defaults)) + + +def filter_oldstyle_options(**options): + filtered = dict() + for key in options.keys(): + newkey = key + for prefix in ['', 'use_', 'opt_allow_', 'opt_']: + newkey = newkey.replace(prefix, '') + filtered[newkey] = options[key] + + return filtered + + +def filter_newstyle_options(func, **options): + allowed = get_options_from_function(func).keys() + filtered = dict() + for key in options.keys(): + for prefix in ['', 'use_', 'opt_', 'opt_allow_']: + if prefix + key in allowed: + filtered[prefix + key] = options[key] + + return filtered + + +def owns_one_constant(I): + """Determines whether I contains the constant one polynomial.""" + for p in I: + if p.is_one(): + return True + return False + + +def want_interpolation_gb(G): + if not G: + return False + if G[0].ring().get_order_code() != OrderCode.lp: + return False + if len(G) != 1: + return False + p = Polynomial(G[0]) + if p.lead_deg() <= 1: + return False + if p.set().n_nodes() > 1000: + return False + return True + + +def ll_is_good(I): + lex_lead = set() + for p in I: + if not p.is_zero(): + m = p.lex_lead() + if m.deg() == 1: + lex_lead.add(next(iter(m.variables())).index()) + if len(lex_lead) >= 0.8 * len(I): + uv = used_vars_set(I).deg() # don't use len here, which will yield 1 + if len(lex_lead) > 0.9 * uv: + if uv - len(lex_lead) > 16: + return "llfirstonthefly" + else: + return "llfirst" + return False + + +def ll_heuristic(d): + d = copy(d) + I = d["I"] + if (not "llfirstonthefly" in d) and (not "llfirst" in d): + hint = ll_is_good(I) + if hint: + d[hint] = True + return d + + +def change_order_heuristic(d): + d_orig = d + d = copy(d) + I = d["I"] + if not I: + return d + switch_table = {OrderCode.lp: OrderCode.dp_asc, OrderCode.dlex: OrderCode. + dp_asc} + if not "other_ordering_first" in d: + # TODO after ll situation might look much different, so heuristic is on + # wrong place + code = next(iter(I)).ring().get_order_code() + if code in switch_table: + max_non_linear = len(I) // 2 + non_linear = 0 + if code == OrderCode.lp: + for p in I: + if p.lead_deg() > 1: + non_linear = non_linear + 1 + if non_linear > max_non_linear: + break + if (non_linear > max_non_linear) or (code != OrderCode.lp): + other_ordering_opts = copy(d_orig) + other_ordering_opts["switch_to"] = switch_table[code] + d["other_ordering_first"] = other_ordering_opts + return d + + +def interpolation_gb_heuristic(d): + d = copy(d) + I = d["I"] + if not d.get("other_ordering_opts", False) and want_interpolation_gb(I): + d["interpolation_gb"] = True + d["other_ordering_first"] = False + return d + + +def linear_algebra_heuristic(d): + d_orig = d + d = copy(d) + I = d["I"] + + def want_la(): + if not I: + return False + n_used_vars = None + bound = None + if next(iter(I)).ring().has_degree_order(): + new_bound = 200 + n_used_vars = used_vars_set(I, bound=new_bound).deg() + if n_used_vars < new_bound: + return True + bound = new_bound + if dense_system(I): + new_bound = 100 + if not (bound and new_bound < bound): + n_used_vars = used_vars_set(I, bound=new_bound).deg() + bound = new_bound + if n_used_vars < bound: + return True + return False + if not (("faugere" in d and (not d["faugere"])) or ("noro" in d and d[ + "noro"])): + if ("faugere" in d and d["faugere"]) or want_la(): + + d["faugere"] = True + if not "red_tail" in d: + d["red_tail"] = False + if not "selection_size" in d: + d["selection_size"] = 10000 + if not ("ll" in d): + d["ll"] = True + + return d + + +def trivial_heuristic(d): + return d + + +class HeuristicalFunction(object): + def __call__(self, *args, **kwds): + complete_dict = copy(kwds) + heuristic = True + try: + heuristic = complete_dict["heuristic"] + except KeyError: + pass + for (k, v) in zip(self.argnames, args): + complete_dict[k] = v + if heuristic: + complete_dict = self.heuristicFunction(complete_dict) + return self.f(**complete_dict) + + def __init__(self, f, heuristic_function): + (self.argnames, self.varargs, self.varopts, self.defaults) = ( + getargspec(f)[:4]) + if hasattr(f, "options"): + self.options = f.options + else: + self.options = dict(zip(self.argnames[-len(self.defaults):], self. + defaults)) + self.heuristicFunction = heuristic_function + self.f = f + self.__doc__ = f.__doc__ + + +def with_heuristic(heuristic_function): + def make_wrapper(f): + wrapped = HeuristicalFunction(f, heuristic_function) + wrapped.__name__ = f.__name__ + return wrapped + return make_wrapper + + +def clean_polys(I): + I = list(set((Polynomial(p) for p in I if not Polynomial(p).is_zero()))) + return I + + +def clean_polys_pre(I): + return (clean_polys(I), None) + + +def gb_with_pre_post_option( + option, pre=None, + post=None, if_not_option=tuple(), + default=False): + def make_wrapper(f): + def wrapper(I, **kwds): + prot = kwds.get("prot", False) + for o in if_not_option: + if (o in kwds and kwds[o]) or (o not in kwds and + groebner_basis.options[o]): + option_set = False + if not "option_set" in locals(): + if option in kwds: + + option_set = kwds[option] + else: + option_set = default + kwds = dict(((o, kwds[o]) for o in kwds if o != option)) + state = None + + if option_set: + if pre: + pre_args = getargspec(pre)[0] + if prot: + print("preprocessing for option:", option) + + local_symbols = copy(locals()) + (I, state) = pre(**dict([(k, v) for (k, v) in + local_symbols.items() if k in pre_args])) + I = f(I, **kwds) + if option_set: + if post: + post_args = getargspec(post)[0] + if prot: + print("postprocessing for option:", option) + local_symbols = copy(locals()) + I = post(**dict([(k, v) for (k, v) \ + in local_symbols.items() if k in post_args])) + + return I + wrapper.__name__ = f.__name__ + wrapper.__doc__ = f.__doc__ + if hasattr(f, "options"): + wrapper.options = copy(f.options) + else: + + wrapper.options = get_options_from_function(f) + + wrapper.options[option] = default + return wrapper + return make_wrapper + + +def redsb_post(I, state): + if I == []: + return [] + else: + return I.minimalize_and_tail_reduce() + + +def minsb_post(I, state): + if I == []: + return [] + else: + return I.minimalize() + + +def invert_all(I): + return [p.map_every_x_to_x_plus_one() for p in I] + + +def invert_all_pre(I): + return (invert_all(I), None) + + +def invert_all_post(I, state): + return invert_all(I) + + +def llfirst_pre(I, prot): + (eliminated, llnf, I) = eliminate(I, on_the_fly=False, prot=prot) + return (I, eliminated) + + +def ll_constants_pre(I): + ll_res = [] + + while len([p for p in I if p.lex_lead_deg() == 1 and + (p + p.lex_lead()).constant()]) > 0: + I_new = [] + ll = [] + leads = set() + for p in I: + if p.lex_lead_deg() == 1: + l = p.lead() + if not (l in leads) and p.is_singleton_or_pair(): + tail = p + l + if tail.deg() <= 0: + ll.append(p) + leads.add(l) + continue + I_new.append(p) + encoded = ll_encode(ll) + reduced = [] + for p in I_new: + p = ll_red_nf_redsb(p, encoded) + if not p.is_zero(): + reduced.append(p) + I = reduced + ll_res.extend(ll) + return (I, ll_res) + + +def variety_size_from_gb(I): + """ + >>> r=Ring(100) + >>> x = r.variable + >>> variety_size_from_gb([]) + 1 + >>> variety_size_from_gb([Polynomial(0, r)]) + 1 + >>> variety_size_from_gb([Polynomial(1, r)]) + 0.0 + >>> variety_size_from_gb([x(1)]) + 1.0 + >>> variety_size_from_gb([x(1), x(2)]) + 1.0 + >>> variety_size_from_gb([x(1), x(2)*x(3)]) + 3.0 + >>> variety_size_from_gb([x(1), x(1)*x(4), x(2)*x(3)]) + 6.0 + >>> variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) + 5.0 + >>> mons = [Monomial([r.variable(i) for i in xrange(100) if i!=j])\ + for j in xrange(100)] + >>> variety_size_from_gb(mons) + 1.2676506002282294e+30 + """ + I = [Polynomial(p) for p in I] + I = [p for p in I if not p.is_zero()] + if len(I) == 0: + return 1 +## # TODO Here's something wrong! See the example with 5 solutions. +## # (reverting for now) +## number_of_used_vars = used_vars_set(I).deg() +## leads = set([p.lead() for p in I]) +## minimal_leads = BooleSet(leads).minimal_elements() +## number_of_used_vars_minimal_leads =\ +## minimal_leads.vars().deg() +## standard_monomials =\ +## minimal_leads.include_divisors().diff(minimal_leads) +## return standard_monomials.size_double()*\ +## 2**(number_of_used_vars-number_of_used_vars_minimal_leads) + + sm = Monomial(used_vars_set(I)).divisors() + for p in I: + m = p.lead() + sm = sm.diff(sm.multiples_of(m)) + return sm.size_double() + + +def other_ordering_pre(I, option_set, kwds): + """ + >>> from brial.blocks import declare_ring + >>> r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) + >>> id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] + >>> groebner_basis(id) + [1] + + """ + if not I: + return (I, None) + + main_kwds = kwds + options = option_set + + old_ring = next(iter(I)).ring() + ocode = old_ring.get_order_code() + try: + new_ring = old_ring.clone(ordering=options["switch_to"]) + + kwds = dict((k, options[k]) for k in options if not (k in ( + "other_ordering_first", "switch_to", "I"))) + kwds["redsb"] = True + I_orig = I + I = groebner_basis([new_ring(poly) for poly in I], **kwds) + variety_size = variety_size_from_gb(I) + + fglm_bound = options.get("fglm_bound") or groebner_basis.options["fglm_bound"] + if variety_size < fglm_bound: + main_kwds["convert_with_fglm_from_ring"] = new_ring + main_kwds["convert_with_fglm_to_ring"] = old_ring + else: + I = [old_ring(poly) for poly in I] + finally: + pass + + return (I, None) + + +def llfirstonthefly_pre(I, prot): + (eliminated, llnf, I) = eliminate(I, on_the_fly=True) + return (I, eliminated) + + +def gauss_on_linear_pre(I, prot): + return (gauss_on_linear(I), None) + + +def easy_linear_polynomials_pre(I): + res = [] + for p in I: + res.append(p) + res.extend(easy_linear_polynomials(p)) + + return (list(set(res)), None) + + +def llfirst_post(I, state, prot, kwds): + eliminated = state + for p in I: + if p.is_one(): + return [p] + else: + if len(eliminated) > 0: + I = list(chain(I, eliminated)) + #redsb just for safety, as don't know how option is set + kwds = copy(kwds) + kwds.update( + dict(llfirst=False, + llfirstonthefly=False, + ll_constants=False, + deg_bound=False, + other_ordering_first=False, + eliminate_identical_variables=False, redsb=True)) + I = groebner_basis( + I, **kwds + ) + return I + + +def ll_constants_post(I, state): + eliminated = state + for p in I: + if p.is_one(): + return [p] + else: + if len(eliminated) > 0: + I = list(chain(I, eliminated)) + #redsb just for safety, as don't know how option is set + return I + + +def result_to_list_post(I, state): + return list(I) + + +def fix_deg_bound_post(I, state): + if isinstance(I, GroebnerStrategy): + return I.all_generators() + else: + return I + + +def incremental_pre(I, prot, kwds): + def sort_key(p): + p = Polynomial(p) + return (p.navigation().value(), -p.deg()) + I = sorted(I, key=sort_key) + inc_sys = [] + kwds = copy(kwds) + kwds['incremental'] = False + + for p in I[:-1]: + inc_sys.append(p) + inc_sys = groebner_basis(inc_sys, **kwds) + if prot: + print("incrementally calculating GB, adding generator:", p) + inc_sys.append(I[:-1]) + return (inc_sys, None) + + +def eliminate_identical_variables_pre(I, prot): + changed = True + ll_system = [] + treated_linears = set() + while changed: + changed = False + rules = dict() + for p in I: + t = p + p.lead() + if p.lead_deg() == 1: + l = p.lead() + if l in treated_linears: + continue + else: + treated_linears.add(l) + if t.deg() > 0: + rules.setdefault(t, []) + leads = rules[t] + leads.append(l) + + def my_sort_key(l): + return l.navigation().value() + for (t, leads) in rules.items(): + if len(leads) > 1: + changed = True + leads = sorted(leads, key=my_sort_key, reverse=True) + chosen = leads[0] + for v in leads[1:]: + ll_system.append(chosen + v) + if len(ll_system) > 0: + ll_encoded = ll_encode(ll_system, reduce=True) + I = set([ll_red_nf_redsb(p, ll_encoded) for p in I]) + return (I, ll_system) + + +@gb_with_pre_post_option("clean_arguments", pre=clean_polys_pre, default=True) +@gb_with_pre_post_option("easy_linear_polynomials", + pre=easy_linear_polynomials_pre, default=True) +@gb_with_pre_post_option("result_to_list", post=result_to_list_post, + default=True) +@with_heuristic(interpolation_gb_heuristic) +@gb_with_pre_post_option("invert", pre=invert_all_pre, + post=invert_all_post, default=False) +@gb_with_pre_post_option("gauss_on_linear", pre=gauss_on_linear_pre, + default=True) +@gb_with_pre_post_option("ll_constants", pre=ll_constants_pre, + post=ll_constants_post, default=True) +@gb_with_pre_post_option("eliminate_identical_variables", + pre=eliminate_identical_variables_pre, post=llfirst_post, default=True) +@with_heuristic(ll_heuristic) +@gb_with_pre_post_option("llfirst", if_not_option=["llfirstonthefly"], + pre=llfirst_pre, post=llfirst_post, default=False) +@gb_with_pre_post_option("llfirstonthefly", pre=llfirstonthefly_pre, + post=llfirst_post, default=False) +@gb_with_pre_post_option("incremental", pre=incremental_pre) +@with_heuristic(change_order_heuristic) +@gb_with_pre_post_option("other_ordering_first", if_not_option=[ + "interpolation_gb"], pre=other_ordering_pre, default=False) +@with_heuristic(linear_algebra_heuristic) +@gb_with_pre_post_option("fix_deg_bound", if_not_option=["interpolation_gb"], + post=fix_deg_bound_post, default=True) +@gb_with_pre_post_option("minsb", post=minsb_post, if_not_option=["redsb", + "deg_bound", "interpolation_gb", "convert_with_fglm_from_ring"], + default=True) +@gb_with_pre_post_option("redsb", post=redsb_post, if_not_option=["deg_bound", + "interpolation_gb", "convert_with_fglm_from_ring"], default=True) + +def groebner_basis(I, heuristic=True, unique_ideal_generator=False, + interpolation_gb=False, clean_and_restart_algorithm=False, + convert_with_fglm_from_ring=None, convert_with_fglm_to_ring=None, + fglm_bound=40000, + modified_linear_algebra=True, preprocessor=None, deg_bound=False, + implementation="Python", full_prot=False, prot=False, draw_matrices=False, + preprocess_only=False, **impl_options): + """Computes a Groebner basis of a given ideal I, w.r.t options.""" + + if not I: + return I + + if full_prot: + prot = True + if prot: + print("number of passed generators:", len(I)) + if not convert_with_fglm_from_ring is None: + from_ring = convert_with_fglm_from_ring + to_ring = convert_with_fglm_to_ring + return _fglm(I, from_ring, to_ring) + + if interpolation_gb: + first = next(iter(I)) + if len(I) != 1 or first.ring().get_order_code() != OrderCode.lp: + raise ValueError + return lex_groebner_basis_for_polynomial_via_variety(first) + if deg_bound is False: + deg_bound = 100000000 + I = [Polynomial(p) for p in I if not p.is_zero()] + if unique_ideal_generator and I: + prod = 1 + for p in I: + prod = (p + 1) * prod + I = [prod + 1] + + if implementation == "Python": + implementation = symmGB_F2_python + else: + implementation = symmGB_F2_C + + # custom preprocessing + if preprocessor: + I = preprocessor(I) + + if preprocess_only: + for p in I: + print(p) + import sys + sys.exit(0) + + def call_algorithm(I, max_generators=None): + return implementation(I, + deg_bound=deg_bound, + full_prot=full_prot, + prot=prot, + max_generators=max_generators, draw_matrices=draw_matrices, + **filter_newstyle_options(implementation, **impl_options)) + + if clean_and_restart_algorithm: + for max_generators in [1000, 10000, 50000, 100000, 200000, 300000, + 400000, None]: + try: + return call_algorithm(I, max_generators=max_generators) + except GeneratorLimitExceeded as e: + I = list(e.strat.all_generators()) + del e.strat + if prot: + print("generator limit exceeded:", max_generators, + "restarting algorithm") + else: + return call_algorithm(I) + + +def build_groebner_basis_doc_string(): + additional_options_from_buchberger = filter_oldstyle_options(** + get_options_from_function(symmGB_F2_python)) + for k in list(additional_options_from_buchberger): + if k in groebner_basis.options: + del additional_options_from_buchberger[k] + + groebner_basis.__doc__ = (groebner_basis.__doc__ + "\nOptions are:\n" + + "\n".join((k + " : " + repr(groebner_basis.options[k]) for k in + groebner_basis.options)) + """ + +Turn off heuristic by setting heuristic=False + Additional options come from the actual buchberger implementation. + In case of our standard Python implementation these are the following: + +""" + + "\n".join((k + " : " + repr(additional_options_from_buchberger[k]) + for k in additional_options_from_buchberger))) + +build_groebner_basis_doc_string() + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/gbrefs.py b/src/sage/rings/polynomial/pbori/gbrefs.py new file mode 100644 index 00000000000..032a3938493 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/gbrefs.py @@ -0,0 +1,173 @@ +from re import sub +import gzip +try: + from StringIO import StringIO +except ImportError: + from io import StringIO +import uu +import re +from types import ModuleType +from .PyPolyBoRi import * +AUTO = "auto" +SINGLE = "single" + + +#def ref_file_name(f): +# name=re.sub("data/","",f) +# name=sub(r"\.py","",name) +# l=name.split("/")[:-1] +def reencode_blocks(block_str): + return str(block_str).replace(",", "_") + + +def parse_blocks(block_str, data): + if block_str == AUTO: + #print "block starts:",data.block_start_hints + return data.block_start_hints + if block_str == SINGLE: + return [] + return [int(i) for i in block_str.split(",")] + + +def load_ref_raw(s): + s = sub("data/", "", s) + s = sub(r"data\.", "", s) + s = sub(r"\.py", "", s) + s = sub(r"\.", "/", s) + + ref_file = "ref/" + s + ".ref" + #print "loading:", ref_file + res_f = open(ref_file) + res = res_f.read() + res_f.close() + return res + + +def load_ref(s, ordering="lp", blocks=SINGLE): + return load_ref_gz_uu(s, ordering, blocks) + + +def ordering_suffix(o, blocks=None): + if o == "lp": + return "" + else: + if re.match("block", o): + return "." + o + "_" + reencode_blocks(blocks) + else: + return "." + o + + +def number_of_declared_vars(data): + try: + return data.number_of_declared_vars + except: + return data.r.ngens() + + +def load_ref_gz_uu(s, o, b): + s = sub("data/", "", s) + s = sub(r"data\.", "", s) + s = sub(r"\.py", "", s) + s = sub(r"\.", "/", s) + + ref_file = "ref/" + s + ordering_suffix(o, b) + ".ref.gz.uu" + #print "loading:", ref_file + res = StringIO() + uu.decode(ref_file, res) + res = res.getvalue() + res = StringIO(res) + res = gzip.GzipFile(fileobj=res, mode="r").read() + res = res.replace(" ", "") + #res_f=open(ref_file) + #res=res_f.read() + #res_f.close() + #print "returning" + return res + +#def load_ref(s): +# return load_ref_raw(s) + +def convert_refs(ref_file_orig): + content = open(ref_file_orig).read() + #buf=StringIO(content) + buf_out = StringIO() + # + zipped = gzip.GzipFile(filename=ref_file_orig, mode="w", fileobj=buf_out) + zipped.write(content) + zipped.close() + val = buf_out.getvalue() + out = open(ref_file_orig + ".gz.uu", "w") + #print val + uu.encode(out_file=out, in_file=StringIO(val)) + out.close() + + +def my_import(name, globals=None, locals=None): + if globals is None: + globals = {} + if locals is None: + locals = {} + #print name + #print locals, globals + mod = __import__(name) + #print dir(mod) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +def dyn_generate(content, name): + module = ModuleType(name) + import_header = """from .PyPolyBoRi import Variable,Monomial, Polynomial, Ring, OrderCode +from itertools import chain +from .blocks import AlternatingBlock,Block,AdderBlock,if_then,HigherOrderBlock,declare_ring as orig_declare_ring,declare_block_scheme,MacroBlock\n +def declare_ring(blocks, context=None): + if context is None: + context=globals() + return orig_declare_ring(blocks,context) +""" + exec(import_header + content, module.__dict__) + if hasattr(module, "ideal"): + module.ideal = [Polynomial(p) for p in module.ideal] + return module + + +def clean_data(data): + r = data.r + for a in dir(data): + if a != "r": + delattr(data, a) + + + #del data.r + #del r +def load_data(file_name, base_dir="./"): + in_file = file_name + #in_file=sub(r"\.py$","",in_file) + #in_file=sub(r"/",".", in_file) + #if not re.match("^data",in_file): + # in_file="data."+in_file + #def x(i): + # return Monomial(Variable(i)) + if not re.match("^data", in_file): + in_file = "data/" + in_file + in_file = sub(r".py$", "", in_file) + module_name = sub("/", r"\.", in_file) + in_file = sub(r"\.", "/", in_file) + in_file = in_file + ".py" + #print in_file + in_file = open(base_dir + in_file).read() + + #print in_file + #return my_import(in_file) + return dyn_generate(in_file, "pb_data") + + +def load_file(file_name): + + in_file = file_name + + in_file = open(in_file).read() + + return dyn_generate(in_file, "pb_data") diff --git a/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py b/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py new file mode 100644 index 00000000000..503406074d6 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py @@ -0,0 +1,616 @@ +# -*- python -*- +from __future__ import print_function + +import sys +import resource +from .gbcore import * +from .memusage import * +from time import time, clock +from .PyPolyBoRi import * +from .blocks import declare_ring, Block +from math import sqrt +from .parallel import groebner_basis_first_finished +from optparse import OptionParser +from .interred import interred + + +# Just for debugging +def print_matrix(A): + res = "" + for i in range(len(A)): + for j in range(len(A[i])): + res += str(A[i][j]) + " " + res += "\n" + return res + + +# TODO: Implement constructor to convert Polynomials to +# GeneralBooleanPolynomial (with e_1 + ... + e_k as coefficient) +class GeneralBooleanPolynomial: + """ + Class to represent Boolean polynomials over F_2^k + """ + + def __init__(self, k, coeff, polynomial): + """ + Construct a GeneralBooleanPolynomial given by coeff * polynomial + Arguments: + k : Number of factors of F_2 + coeff : Array containing natural numbers in {0, ..., k-1} representing an element of F_2^k as a set + polynomial : Polynomial + """ + #print "type of polynomials", type(polynomial) + #print "polynomials is int?", isinstance(polynomial, int) + assert isinstance(polynomial, Polynomial) or isinstance(polynomial, + Monomial) or isinstance(polynomial, Variable) or isinstance( + polynomial, int) + self.polys = [] + self.k = k + self.ring = polynomial.ring() + for i in range(k): + if i in coeff: + self.polys.append(polynomial) + else: + self.polys.append(Polynomial(0, self.ring)) + + def __len__(self): + """ + Returns the number of factors k in the product of the underlying ring F_2^k + """ + return self.k + + def __getitem__(self, k): + """ + Return the k-th component (i.e. the projection to the k-th factor) + """ + return self.polys[k] + + def __setitem__(self, k, value): + """ + Sets the k-th component (i.e. the projection to the k-th factor) + """ + self.polys[k] = value + + def __eq__(self, other): + """ + Tests equality by testing that + - both objects are defined over the same ring (i.e. the number of factors is the same) + - the objects are equal in each component + """ + if not len(self) == len(other): + return False + for i in range(len(self)): + if self[i] != other[i]: + return False + return True + + def __ne__(self, other): + return not self == other + + def __str__(self): + """ + Returns a representation of the polynomial as string + """ + res = "" + # Build a list of all terms occurring + terms = set([]) + for i in range(self.k): + if not isinstance(self.polys[i], Polynomial): + assert isinstance(self.polys[i], Monomial) or isinstance(self. + polys[i], Variable) + self[i] = Polynomial(self[i]) + terms = terms.union(set(self[i].terms())) + # Sort the terms + terms = list(terms) + terms.sort() + # Determine the coefficient for each term and build up the string + for t in terms: + comps = [j for j in range(self.k) if t in set(Polynomial(self[j]) + .terms())] + if len(comps) == self.k: + # We use this simplified notation of the coefficient it 1 + res += str(t) + " + " + else: + res += str(comps) + " * " + str(t) + " + " + res = res[0:len(res) - 3] + if res == "": + return "0" + else: + return res + + def __add__(self, other): + """ + Addition of two GeneralBooleanPolynomial + """ + if not len(self) == len(other): + print("Cannot add polynomials defined over different rings") + print("Len(self)=", len(self)) + print("Len(other)=", len(other)) + assert len(self) == len(other) + sum = GeneralBooleanPolynomial(self.k, [], Polynomial(0, self.ring)) + for i in range(self.k): + sum[i] = self[i] + other[i] + return sum + + def __mul__(self, other): + """ + Multiplication of two GeneralBooleanPolynomial + """ + if not len(self) == len(other): + print("Cannot multiply polynomials defined over different rings") + print("Len(self)=", len(self)) + print("Len(other)=", len(other)) + assert len(self) == len(other) + prod = GeneralBooleanPolynomial(self.k, [], Polynomial(0, self.ring)) + for i in range(self.k): + prod[i] = Polynomial(self[i]) * Polynomial(other[i]) + return prod + + def __sub__(self, other): + """ + Subtraction of two GeneralBooleanPolynomial + """ + if not len(self) == len(other): + print("Cannot subtract polynomials defined over different rings") + print("Len(self)=", len(self)) + print("Len(other)=", len(other)) + assert len(self) == len(other) + sub = GeneralBooleanPolynomial(self.k, [], Polynomial(1, self.ring)) + for i in range(self.k): + sub[i] = self[i] - other[i] + return sub + + def lc(self): + """ + Returns leading coefficient as constant GeneralBooleanPolynomial + """ + return GeneralBooleanPolynomial(self.k, self.lc_as_set(), + Polynomial(1, self.ring)) + + def lc_as_set_array(self): + """ + Returns leading coefficient as array containing the indices of the + non-zero components of the leading coefficient. + """ + max_term = 1 + for i in range(len(self)): + if not self[i].is_zero(): + if self[i].lead() > max_term: + max_term = self[i].lead() + comps = [j for j in range(len(self)) if max_term + in set(self[j].terms())] + return comps + + def lc_as_set(self): + """ + Returns leading coefficient as set containing the indices of the + non-zero components of the leading coefficient. + """ + return set(self.lc_as_set_array()) + + def lc_binary(self): + """ + Returns leading coefficient as array containing the integers 0 or 1 + representing the leading coefficient in a binary form. + """ + lc_set = self.lc_as_set() + lc_binary = [0] * len(self) + for i in range(len(self)): + if i in lc_set: + lc_binary[i] = 1 + return lc_binary + + def lt(self): + """ + Leading term in form of a GeneralBooleanPolynomial + """ + max_term = 1 + for i in range(self.k): + if not self[i].is_zero(): + if self[i].lead() > max_term: + max_term = self[i].lead() + comps = [j for j in range(self.k) if max_term in set(self[j].terms())] + return GeneralBooleanPolynomial(self.k, comps, max_term) + + def lm(self): + """ + Leading monomial in form of a GeneralBooleanPolynomial + """ + max_term = 1 + contains_non_zero_term = False + for i in range(len(self)): + if not self[i].is_zero(): + contains_non_zero_term = True + if self[i].lead() > max_term: + max_term = self[i].lead() + if not contains_non_zero_term: + raise ValueError("lm of zero polynomial is not defined") + return GeneralBooleanPolynomial(self.k, [i for i in range(self.k)], + max_term) + + def constant_part_binary(self): + """ + Constant part as binary tuple indicading which coefficients are non-zero + """ + comps = [] + for i in range(len(self)): + if self[i].has_constant_part(): + comps.append(1) + else: + comps.append(0) + return comps + + def constant_part_as_set_array(self): + """ + Constant part as array containing the indices of the non-zero coefficients of the constant part (sorted increasingly) + """ + res = [] + for i in range(len(self)): + if self[i].has_constant_part(): + res.append(i) + return res + + def constant_part_as_set(self): + """ + Constant part as set containing the indices of the non-zero coefficients of the constant part + """ + return set(self.constant_part_as_set_array()) + + def constant_part(self): + """ + Constant part as GeneralBoolenPolynomial + """ + res = GeneralBooleanPolynomial(len(self), [], Polynomial(0, self.ring)) + for i in range(len(self)): + if self[i].has_constant_part(): + res[i] = Polynomial(1, self.ring) + else: + res[i] = Polynomial(0, self.ring) + return res + + def to_expanded_polynomial_ring(self, new_variables): + """ + Returns a representation in form of a Polynomial over a ring with + additional variables, one for each factor in the product of fields + F_2^k + """ + assert self.k == len(new_variables) + return sum(new_variables[i] * self.polys[i] for i in range(self.k)) + + def is_monomial(self): + """ + Test if self is a Monomial + """ + # Build a list of all terms occurring + terms = set([]) + for i in range(self.k): + if not isinstance(self.polys[i], Polynomial): + assert isinstance(self.polys[i], Monomial) or isinstance(self. + polys[i], Variable) + self[i] = Polynomial(self[i]) + terms = terms.union(set(self[i].terms())) + if len(terms) > 1: + return False + else: + return True + + def is_zero(self): + """ + Tests if self is zero + """ + for i in range(len(self)): + if self[i] != 0: + return False + return True + + def monomial(self): + """ + Returns a PolyBoRi Monomial representing the leading monomial of self, where self should be a monomial + """ + assert self.is_monomial() + for i in range(self.k): + if self.polys[i] != Polynomial(0, self.ring): + return self.polys[i].lead() + return Polynomial(0, self.ring) + + def divides(self, other): + """ + Tests if self divides other + """ + assert len(self) == len(other) + assert (self.is_monomial() and other.is_monomial()) + self_divides_other = True + for i in range(len(self)): + if self[i] == 0: + if other[i] != 0: + return False + else: + continue + if self[i] == 1: + continue + else: + if other[i] == 1: + return False + + if other[i] == 0: + continue + + assert other[i] == other[i].lead() + other[i] = other[i].lead() + if not other[i] in (Polynomial(self.polys[i]).lead()).divisors(): + return False + return True + + +# Simple Gaus algorithm +# Should really be replaced by something faster (maybe from PolyBoRi) +def triangulate_over_F2(A, b): + assert len(A) == len(b) + n = len(b) + m = len(A[0]) + print("n: ", n, "m: ", m) + print("A, b", A, b) + for i in range(0, min(n, m)): # Row + print("i", i) + if A[i][i] == 0: + # permutate rows + changed = False + for l in range(i, n): + if A[l][i] != 0: + for k in range(n): + A[l][k], A[i][k] = A[i][k], A[l][k] + b[l], b[i] = b[i], b[l] + changed = True + if not changed: + return -1 + for j in range(0, i): + if A[i][j] != 0: + for k in range(j, n): + A[i][k] += A[i - 1][k] + b[i] += b[i - 1] + res_A = [[A[i][j] % 2 for j in range(m)] for i in range(n)] + res_b = [b[i] % 2 for i in range(n)] + return (res_A, res_b) + + +def projection_of_expanded_polynomial(f, e, e_vars): + """ + Compute the projection of the expanded polynomial f to the component + corresponding to the variable e (which is part of e_vars) + """ + for v in e_vars: + if v == e: + # Substitute v by 1 + f = Polynomial(f.set().subset0(v.index())) + Polynomial(f.set(). + subset1(v.index())) + else: + # Substitute v by 0 + f = Polynomial(f.set().subset0(v.index())) + return f + + +def expanded_polynomial2general_polynomial(polynomial, new_variables, ring): + """ + Returns a GeneralBooleanPolynomial associated to a Polynomial (polynomial) in + additional variables (new_variables), where the + GeneralBooleanPolynomial in obtained from the projections of polynomial + to the factors of F_2^k (by specialization of the additional variables) + """ + comps = [projection_of_expanded_polynomial(polynomial, e, new_variables) + for e in new_variables] + sum2 = GeneralBooleanPolynomial(len(new_variables), [0] * len( + new_variables), Polynomial(0, ring)) + for i in range(len(new_variables)): + sum2[i] = comps[i] + return sum2 + + +def reduce_general_boolean_polynomial(F, polynomial): + """ + Computes the reduction of polynomial via the ideal given by F + """ + r = polynomial + s = len(F) + k = len(polynomial) + h = [GeneralBooleanPolynomial(len(polynomial), [0] * len(polynomial), + Polynomial(0, self.ring))] * s + # Indices i where leading monomial of F[i] divided leading monomial of r + + def calc_Is(): + ret = [] + for i in range(s): + if (F[i].is_zero() and not r.is_zero()): + continue + if (F[i].lm()).divides(r.lm()): + ret.append(i) + return ret + + def calc_X(): + ret = [] + for i in range(len(Is)): + ret.append((r.lm()).monomial() / (F[Is[i]].lm()).monomial()) + return ret + + def included(i, set): + if i in set: + return 1 + else: + return 0 + + Is = calc_Is() + X = calc_X() + + for f in F: + assert len(f) == len(polynomial) + + lm_polynomial = (polynomial.lm()).monomial() + lc_polynomial_binary = polynomial.lc_binary() + + while len(Is) != 0: + exp = [F[i].lm() for i in range(s)] + matrix = [[included(i, F[j].lc_as_set()) for j in Is] for i in range( + k)] + + # Compute solution + coeff = [[0] * len(Is) for j in range(k)] + for j in range(k): + if lc_polynomial_binary[j]: + coeff[j][0] = 1 + + sum = GeneralBooleanPolynomial(k, [0] * k, Polynomial(0, self.ring)) + for i in range(len(Is)): + c = [coeff[l][i] for l in range(k)] + c_set = [l for l in range(k) if coeff[l][i] == 1] + poly1 = GeneralBooleanPolynomial(k, c_set, X[i]) + + sum += GeneralBooleanPolynomial(k, c_set, X[i]) * F[Is[i]].lt() + + if polynomial.lt() == sum: + r -= sum + else: + break + + if r.is_zero(): + return r + + Is = calc_Is() + X = calc_X() + return r + + +def stratified(I): + """ + Tests if I does no contain two polynomials with the same leading monomials + """ + leading_monomials = [] + for p in I: + if p.is_zero(): + continue + lm = p.lm() + if lm in leading_monomials: + return False + leading_monomials.append(lm) + return True + + +def build_dict_from_array_of_extended_polynomials(A, e_vars): + new_dict = {} + for p in A: + new_dict[p] = expanded_polynomial2general_polynomial(p, e_vars) + return new_dict + + +def stratify_dict_I_gb_I(dict, e_vars, debug=0): + """ + Wrapper (calls either stratify_dict_I_gb_I_our_alg or stratify_dict_I_gb_I_Inoue + """ + #return stratify_dict_I_gb_I_Inoue(dict, e_vars, debug) + return stratify_dict_I_gb_I_our_alg(dict, e_vars, debug) + + +def stratify_dict_I_gb_I_our_alg(dict, e_vars, debug=0): + """ + Build a stratified Groebner bases for dict + """ + # Ideal for the polynomials of the new basis + A = [] + # and their leading monomials + LMs = [] + new_dict = {} + + + while len(dict) > 0: + # We copy the keys of dict into I in order to sort them. + # This way the elements are traversed in a unique order. + I = sorted(dict.keys()) + p = I[0] + + p_gb = dict[p] + + del dict[p] + + if debug > 1: + print("Processing p", p) + print("A before proceeding", A) + + if p_gb.is_zero(): + LMs.append(Polynomial(0, self.ring)) + A.append(p) + if debug > 1: + print("Adding p that becomes zero") + continue + + p_gb_lm = p_gb.lm() + + # If the leading monomial of p does not coincide with any of + # polynomials already in A, then add p to A + if not p_gb_lm in LMs: + A.append(p) + LMs.append(p_gb_lm) + if debug > 1: + print("Appending", p, "since its lm is not contained in A yet") + continue + + # Index of p_gb_lm in LMs + i = LMs.index(p_gb_lm) + + # Polynomial in A with the same lm as p + b = A[i] + + # The Polynomial we want to add to A while keeping A stratified + r = p + + b_gb = expanded_polynomial2general_polynomial(b, e_vars) + r_gb = expanded_polynomial2general_polynomial(r, e_vars) + + # Leading coefficients as GeneralBooleanPolynomial + lc_b_gb = b_gb.lc() + lc_r_gb = r_gb.lc() + unit = GeneralBooleanPolynomial(len(e_vars), [o for o in range(len( + e_vars))], Polynomial(1, p.ring())) + + b1_gb = b_gb * (unit + lc_r_gb) + r_gb + r2_gb = r_gb * (unit + lc_r_gb + lc_r_gb * lc_b_gb) + lc_r_gb * b_gb + + b1 = b1_gb.to_expanded_polynomial_ring(e_vars) + r2 = r2_gb.to_expanded_polynomial_ring(e_vars) + + A[i] = b1 + if debug > 1: + print("New polynomial in A (replaced)", A[i]) + + if r2 != 0 and r2 not in A: + dict[r2] = r2_gb + + return build_dict_from_array_of_extended_polynomials(A, e_vars) + + +def stratify_dict_I_gb_I_Inoue(dict, e_vars, debug=0): + """ + Reimplementation of a simple algorithm of Inoue from BGSet + """ + # Ideal for the polynomials of the new basis + A = [] + new_dict = {} + while len(dict) > 0: + p = dict.keys()[0] + p_gb = dict[p] + del dict[p] + + if p_gb.is_zero(): + A.append(p) + continue + + p_gb_lm = p_gb.lm() + + for f in dict.keys(): + f_gb = dict[f] + + if f_gb.is_zero(): + continue + + if p_gb_lm == f_gb.lm(): + p = p + f + del dict[f] + A.append(p) + return build_dict_from_array_of_extended_polynomials(A, e_vars) diff --git a/src/sage/rings/polynomial/pbori/heuristics.py b/src/sage/rings/polynomial/pbori/heuristics.py new file mode 100644 index 00000000000..aba71378bd9 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/heuristics.py @@ -0,0 +1,36 @@ +from .PyPolyBoRi import Polynomial, gauss_on_polys +from .nf import symmGB_F2_python +from .interpolate import variety_lex_leading_terms, nf_lex_points + + +def dense_system(I): + I = (Polynomial(p) for p in I) + I = (p for p in I if not p.is_zero()) + for p in I: + d = p.deg() + if p.deg() == 1: + continue + else: + try: + if len(p) > 2 ** d + 5: + return True + except OverflowError: + return True + return False + + +def gauss_on_linear(I): + I = (Polynomial(p) for p in I) + linear = [] + non_linear = [] + for p in I: + if p.is_zero(): + continue + if p.deg() <= 1: + linear.append(p) + else: + non_linear.append(p) + if len(linear) == 0: + return non_linear + linear = list(gauss_on_polys(linear)) + return linear + non_linear diff --git a/src/sage/rings/polynomial/pbori/interpolate.py b/src/sage/rings/polynomial/pbori/interpolate.py new file mode 100644 index 00000000000..41b87777a30 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/interpolate.py @@ -0,0 +1,132 @@ +# Copyright (c) 2005-2007 by The PolyBoRi Team + +from __future__ import print_function + +import sys +from .PyPolyBoRi import * +from .randompoly import gen_random_poly +from random import Random +try: + from time import process_time as clock +except ImportError: + from time import clock + +generator = Random() + + +def add_up_poly_list(l, init): + v = BoolePolynomialVector() + for p in l: + v.append(p) + return add_up_polynomials(v, init) + + +def bench_interpolate(degree, nvariables, points): + + d = degree + v = nvariables + c = points + h = len(points) / 2 + terms = set(c.terms()) + part1 = generator.sample(terms, h) + part1 = add_up_poly_list(part1, Polynomial(c.ring().zero())) + part2 = c + part1 + p = part1 + q = part2 + assert part1.set().intersect(part2).empty() + c1 = clock() + res2 = interpolate_smallest_lex(p, q) + c2 = clock() + print("finished interpolate_smallest_lex(p,q),len:", len(res2), "time", c2 + - c1) + c1 = clock() + res1 = interpolate(p, q) + c2 = clock() + print("finished interpolate(p,q)" + len("_smallest_lex") * " " + ",len:", + res1.set().size_double(), "time:", c2 - c1) + return res2 + + +def nf_lex_points(f, p): + f = Polynomial(f) + p = BooleSet(p) + z = f.zeros_in(p) + return interpolate_smallest_lex(z, p.diff(z)) + + +def gen_random_monomial(): + d = generator.randrange(min(6, v + 1)) + variables = generator.sample(range(v), d) + variables = sorted(variables, key=top_index, reverse=True) + m = variables[0] + for x in variables[1:]: + m = x * m + return m + + +def gen_random_polynomial(ring, max_len=50): + vec = BoolePolynomialVector() + for i in range(max_len): + vec.append(gen_random_monomial(ring)) + return add_up_polynomials(vec, Polynomial(ring.zero())) + + +def gen_random_o_z(points, points_p): + k = generator.randrange(len(points) + 1) + ones = generator.sample(points, k) + vec = BoolePolynomialVector() + for p in ones: + vec.append(p) + ones = add_up_polynomials(vec, Polynomial(points_p.ring().zero())) + return interpolate_smallest_lex(points_p.set().diff(ones), ones) + + +def variety_lex_leading_terms(points, variables): + ring = variables.ring() + standards = BooleSet(ring.zero()) + assert type(points) == BooleSet, "Points needs to be a BooleSet" + points_tuple = tuple(points) + myvars_div = variables.divisors() + myvars_iter = iter(myvars_div) + if points != myvars_div: + standards = BooleSet(ring.one()) + len_standards = len(standards) + standards_old = standards + while len_standards < len(points): + standards = standards.union(gen_random_o_z(points_tuple, points)) + + if standards_old != standards: + standards = BooleSet(standards).include_divisors() + len_standards = len(standards) + standards_old = standards + + leading_terms = BooleSet(myvars_div.diff(standards)).minimal_elements() + return leading_terms + + +def lex_groebner_basis_points(points, variables): + leads = variety_lex_leading_terms(points, variables) + return [nf_lex_points(l, points) + l for l in leads] + + +def lex_groebner_basis_for_polynomial_via_variety(p): + variables = p.vars_as_monomial() + return lex_groebner_basis_points(p.zeros_in(variables.divisors()), + variables) +if __name__ == '__main__': + nvariables = 100 + r = declare_ring([Block("x", nvariables)]) + for number_of_points in (100, 500, 1000, 2000, 3000, 4000, 5000, 10000, + 20000, 50000, 100000): + print("----------") + print("number_of_points:", number_of_points) + print("generate points") + points = gen_random_poly(number_of_points, nvariables, [Variable(i) + for i in range(nvariables)]) + print("points generated") + bench_interpolate(nvariables, nvariables, points) + vars_mon = Monomial(r) + for i in reversed(range(nvariables)): + vars_mon = vars_mon * Variable(i, r) + print(len(variety_lex_leading_terms(points, vars_mon)), + "elements in groebner basis") diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py new file mode 100644 index 00000000000..f7f03e7d612 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/interred.py @@ -0,0 +1,29 @@ +from .PyPolyBoRi import GroebnerStrategy, Polynomial, ReductionStrategy + + +def interred(l, completely=False): + """computes a new generating system (g1, ...,gn), + spanning the same ideal modulo field equations. + The system is interreduced: For i!=j: + gi.lead() does not divide any leading term of gj. + If completely is set to True, then also terms in the + tail are not reducible by other polynomials. + """ + l = [Polynomial(p) for p in l if not p == 0] + if not l: + return [] + ring = l[0].ring() + l_old = None + l = tuple(l) + while l_old != l: + l_old = l + l = sorted(l, key=Polynomial.lead) + g = ReductionStrategy(ring) + if completely: + g.opt_red_tail = True + for p in l: + p = g.nf(p) + if not p.is_zero(): + g.add_generator(p) + l = tuple([e.p for e in g]) + return list(l) diff --git a/src/sage/rings/polynomial/pbori/intersect.py b/src/sage/rings/polynomial/pbori/intersect.py new file mode 100644 index 00000000000..be91eeccf62 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/intersect.py @@ -0,0 +1,45 @@ +# +# intersect.py +# PolyBoRi +# +# Created by Michael Brickenstein on 2008-09-24. +# Copyright 2008 The PolyBoRi Team +# + +from .gbcore import groebner_basis +from .statistics import used_vars_set +from itertools import chain + + +def intersect(i, j, **gb_opts): + """ + This functions intersects two ideals. The first ring variable is used as helper variable for this + intersection. It is assumed, that it doesn't occur in the ideals, and that we have an elimination ordering + for this variables. Both assumptions are checked. + >>> from brial.frontend import declare_ring + >>> from brial import Block + >>> r=declare_ring(Block("x", 1000), globals()) + >>> x = r.variable + >>> intersect([x(1),x(2)+1],[x(1),x(2)]) + [x(1)] + """ + if not i or not j: + return [] + + uv = used_vars_set(i) * used_vars_set(j) + t = next(iter(i)).ring().variable(0) + if uv.reducible_by(t): + raise ValueError("First ring variable has to be reserved as helper variable t") + if not t > uv: + raise ValueError("need elimination ordering for first ring variable") + gb = groebner_basis(list(chain((t * p for p in i), ((1 + t) * p for p in j + ))), **gb_opts) + return [p for p in gb if p.navigation().value() > t.index()] + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/intpolys.py b/src/sage/rings/polynomial/pbori/intpolys.py new file mode 100644 index 00000000000..ac7a1445c00 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/intpolys.py @@ -0,0 +1,87 @@ +if __name__ == '__main__': + from sys import path as search_path + from os import path as file_path + search_path.append(file_path.join(file_path.dirname(__file__), '..')) + +from .PyPolyBoRi import Monomial, Polynomial, BooleSet, BooleConstant +from .PyPolyBoRi import Variable as VariableType + + +class IntegerPolynomial(object): + """Polynomial with positive integer coefficients""" + + def __init__(self, boolean_polys): + super(IntegerPolynomial, self).__init__() + if not isinstance(boolean_polys, dict): + boolean_polys = dict([(0, Polynomial(boolean_polys))]) + self.boolean_polys = boolean_polys + + def __coerce__(self, other): + #TODO handle long + if isinstance(other, int) and other >= 0: + i = 0 + res = [] + while 2 ** i <= other: + and_ = 2 ** i & other + if and_: + res.append(i) + return (self, IntegerPolynomial(dict([(i, BooleConstant(1)) for i + in res]))) + if not isinstance(other, IntegerPolynomial): + other = Polynomial(other) + return (self, IntegerPolynomial(dict([(0, other)]))) + + def __add__(self, other): + """ + >>> from brial import * + >>> r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS + >>> p=IntegerPolynomial(x(1)) + >>> p + {0: x(1)} + >>> p=p+p;p + {1: x(1)} + >>> p=p+x(2); p + {0: x(2), 1: x(1)} + >>> p+p + {1: x(2), 2: x(1)} + """ + if not isinstance(other, IntegerPolynomial): + (self, other) = coerce(self, other) + return self + other + assert isinstance(other, IntegerPolynomial) + + def add_simple_poly(p, i): + p = Polynomial(p) + if p.is_zero(): + return + res_p = Polynomial(res.get(i, Polynomial(0, p.ring()))) + res[i] = res_p + p + if res[i].is_zero(): + del res[i] + inter = BooleSet(res_p).intersect(BooleSet(p)) + add_simple_poly(inter, i + 1) + return + res = dict(self.boolean_polys.items()) + for (k, p) in other.boolean_polys.items(): + add_simple_poly(p=p, i=k) + return IntegerPolynomial(res) + + def __unicode__(self): + return unicode(self.boolean_polys) + + def __str__(self): + return str(self.boolean_polys) + + def __repr__(self): + try: + return unicode(self) + except NameError: + return str(self) + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py new file mode 100644 index 00000000000..16defde38ee --- /dev/null +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -0,0 +1,290 @@ +from __future__ import print_function + +from .PyPolyBoRi import * +from .statistics import used_vars_set + +from .rank import rank + +from functools import reduce + +lead_index = top_index + +#(p): +# return iter(p.lex_lead()).next().index()#first index + +def combine(reductors, p, reduce=None): + p_nav = p.navigation() + assert p_nav.value() < reductors.navigation().value() + p_else = BooleSet(p_nav.else_branch(), p.ring()) + if reduce: + p_else = reduce(p_else, reductors) + return if_then_else(p_nav.value(), reductors, p_else) + + +def llredsb_Cudd_style(polys): + + if polys: + reductors = Polynomial(polys[0].ring().one()).set() + else: + reductors = None + + linear_lead = sorted(polys, key=lead_index, reverse=True) + assert len(set([p.lex_lead() for p in linear_lead])) == len(polys) + assert len([p for p in polys if p.constant()]) == 0 + assert len([p for p in polys if p.lex_lead_deg() == 1]) == len(polys) + assert len(set([p.navigation().value() for p in polys])) == len(polys) + for p in linear_lead: + reductors = combine(reductors, p, reduce=ll_red_nf_redsb) + return reductors + + +def ll_encode(polys, reduce=False, prot=False, reduce_by_linear=True): + polys = [Polynomial(p) for p in polys] + linear_lead = sorted(polys, key=lead_index, reverse=True) + assert len(set([p.lex_lead() for p in linear_lead])) == len(polys) + assert len([p for p in polys if p.constant()]) == 0 + assert len([p for p in polys if p.lex_lead_deg() == 1]) == len(polys) + assert len(set([p.navigation().value() for p in polys])) == len(polys) + if (not reduce) and reduce_by_linear: + linear_polys = [p for p in polys if p.deg() == 1] + if linear_polys: + linear_ll = ll_encode(linear_polys, reduce=True, + reduce_by_linear=False) + polys = [p.lex_lead() + ll_red_nf_redsb(p + p.lex_lead(), + linear_ll) for p in polys] + if reduce: + reduce = ll_red_nf_redsb + else: + reduce = None + + + if polys: + reductors = Polynomial(polys[0].ring().one()).set() + else: + reductors = None + + last = None + counter = 0 + for p in linear_lead: + + if prot: + counter = counter + 1 + progress = (counter * 100) / len(linear_lead) + if last != progress: + print(str(progress) + "%") + last = progress + reductors = combine(reductors, p, reduce=reduce) + return reductors + + +def eliminate(polys, on_the_fly=False, prot=False, reduction_function=None, + optimized=True): + """There exists an optimized variant, which reorders the variable in a different + ring. + """ + polys = [Polynomial(p) for p in polys] + rest = [] + linear_leads = [] + linear_leading_monomials = set() + for p in polys: + if p.is_zero(): + continue + lm = p.lex_lead() + if lm.deg() == 1: + + if not (lm in linear_leading_monomials): + linear_leading_monomials.add(lm) + linear_leads.append(p) + else: + rest.append(p) + else: + rest.append(p) + if len(linear_leads) == 0: + def identity(p): + return p + return (linear_leads, identity, rest) + if reduction_function is None: + if on_the_fly: + if optimized: + reduction_function = ll_red_nf_noredsb_single_recursive_call + else: + reduction_function = ll_red_nf_noredsb + else: + reduction_function = ll_red_nf_redsb + + def llnf(p): + return reduction_function(p, reductors) + reduced_list = [] + if optimized: + (llnf, reduced_list) = eliminate_ll_ranked( + linear_leads, + rest, + reduction_function=reduction_function, + reduce_ll_system=(not on_the_fly), + prot=prot) + else: + reductors = ll_encode(linear_leads, reduce=(not on_the_fly), prot=prot) + for p in rest: + p = reduction_function(p, reductors) + if p.is_one(): + reduced_list = [p] + break + else: + reduced_list.append(p) + + return (linear_leads, llnf, reduced_list) + + +def construct_map_by_indices(to_ring, idx_mapping): + v = BoolePolynomialVector((max(idx_mapping.keys()) + 1) * [to_ring.zero()]) + for (from_idx, to_idx) in idx_mapping.items(): + val = to_ring.variable(to_idx) + v[from_idx] = val + return v + + +def eliminate_ll_ranked(ll_system, to_reduce, + reduction_function=ll_red_nf_noredsb, + reduce_ll_system=False, prot=False): + + assert(ll_system) + from_ring = ll_system[0].ring() + + ll_ranks = rank(ll_system) + add_vars = set(used_vars_set(to_reduce).variables()).difference(ll_ranks. + keys()) + for v in add_vars: + ll_ranks[v] = -1 + + #pushing variables ignored by ll to the front means, + #that the routines will quickly eliminate them + #and they won't give any overhead + def sort_key(v): + return (ll_ranks[v], v.index()) + sorted_vars = sorted(ll_ranks.keys(), key=sort_key) + + def var_index(v): + return next(iter(Monomial(v).variables())).index() + #sorted_var_indices=[var_index(v) for v in sorted_vars] + to_ring = Ring(len(sorted_vars)) + map_back_indices = dict([(i, var_index(v)) for (i, v) in enumerate( + sorted_vars)]) + map_from_indices = dict([(var_index(v), i) for (i, v) in enumerate( + sorted_vars)]) + #dict([(v,k) for (k,v) in enumerate(sorted_var_indices)]) + var_names = [str(v) for v in sorted_vars] + try: + for (i, v) in enumerate(sorted_vars): + assert var_names[i] == str(v), (var_names[i], v, var_index(v), i) + # _set_variable_name(to_ring, i, var_names[i] + "TO") + finally: + pass + try: + map_from_vec = construct_map_by_indices(to_ring, map_from_indices) + finally: + pass + map_back_vec = construct_map_by_indices(from_ring, map_back_indices) + + def map_from(p): + res = substitute_variables(to_ring, map_from_vec, p) + # assert str(p)==str(res), (str(p), str(res), list(map_from_vec), + # list(map_back_vec)) + return res + + def map_back(p): + return substitute_variables(from_ring, map_back_vec, p) + try: + ll_opt_encoded = ll_encode([map_from(p) for p in ll_system], + prot=False, + reduce=reduce_ll_system) + + def llnf(p): + return map_back(reduction_function(map_from(p), ll_opt_encoded)) + opt_eliminated = [llnf(p) for p in to_reduce] + finally: + pass + return (llnf, opt_eliminated) + + +class RingMap(object): + """Define a mapping between two rings by common variable names. + + >>> from brial.frontend import * + >>> to_ring = declare_ring([Block("x", 10)], globals()) + >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + >>> mapping = RingMap(to_ring, from_ring) + >>> (x(1)+1).navigation().value() + 6 + >>> mapping(x(1)+1) + x(1) + 1 + >>> mapping(x(1)+1).navigation().value() + 1 + >>> mapping.invert(mapping(x(1)+1)) + x(1) + 1 + >>> mapping.invert(mapping(x(1)+1)).navigation().value() + 6 + >>> mapping(y(1)+1) + Traceback (most recent call last): + ... + RuntimeError: Operands come from different manager. + """ + + def __init__(self, to_ring, from_ring): + """Initialize map by two given rings. + + >>> from brial.frontend import * + >>> to_ring = declare_ring([Block("x", 10)], globals()) + >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + >>> mapping = RingMap(to_ring, from_ring) + >>> mapping(x(1)+1) + x(1) + 1 + """ + + def vars(ring): + return [ring.variable(i) for i in range(ring.n_variables())] + + def indices(vars): + return dict([(str(v), idx) for (idx, v) in enumerate(vars)]) + + self.to_ring = to_ring + self.from_ring = from_ring + to_vars = vars(to_ring) + from_vars = vars(from_ring) + to_indices = indices(to_vars) + from_indices = indices(from_vars) + common = list(set(to_indices.keys()) & set(from_indices.keys())) + + to_map = list(from_vars) + for elt in common: + to_map[from_indices[elt]] = to_vars[to_indices[elt]] + + from_map = list(to_vars) + for elt in common: + from_map[to_indices[elt]] = from_vars[from_indices[elt]] + + self.to_map = BoolePolynomialVector(to_map) + self.from_map = BoolePolynomialVector(from_map) + + def __call__(self, poly): + """Execute the map to change rings. + + >>> from brial.frontend import * + >>> to_ring = declare_ring([Block("x", 10)], globals()) + >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + >>> mapping = RingMap(to_ring, from_ring) + >>> mapping(x(1)+1) + x(1) + 1 + """ + return substitute_variables(self.to_ring, self.to_map, poly) + + def invert(self, poly): + """Inverted map to initial ring. + + >>> from brial.frontend import * + >>> to_ring = declare_ring([Block("x", 10)], globals()) + >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + >>> mapping = RingMap(to_ring, from_ring) + >>> mapping.invert(mapping(x(1)+1)) + x(1) + 1 + """ + return substitute_variables(self.from_ring, self.from_map, poly) diff --git a/src/sage/rings/polynomial/pbori/memusage.py b/src/sage/rings/polynomial/pbori/memusage.py new file mode 100644 index 00000000000..521442c83bb --- /dev/null +++ b/src/sage/rings/polynomial/pbori/memusage.py @@ -0,0 +1,65 @@ +import os + +_proc_status = '/proc/%d/status' % os.getpid() +#_scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, +# 'KB': 1024.0, 'MB': 1024.0*1024.0} + +_scale = {'kB': 1, 'mB': 1024, 'gB': 1024 * 1024, + 'KB': 1, 'MB': 1024, 'GB': 1024 * 1024} + + +def _VmB(VmKey): + '''Private. + ''' + global _proc_status, _scale + # get pseudo file /proc//status + try: + t = open(_proc_status) + v = t.read() + t.close() + except: + return float('nan') # non-Linux? + # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' + i = v.index(VmKey) + v = v[i:].split(None, 3) # whitespace + if len(v) < 3: + return float('nan') # invalid format? + # convert Vm value to bytes + # return float(v[1]) * _scale[v[2]] + return int(v[1]) * _scale[v[2]] + + +def memory(since=0): + '''Return memory usage in kilobytes. + ''' + return _VmB('VmSize:') - since + + +def resident(since=0): + '''Return resident memory usage in kilobytes. + ''' + return _VmB('VmRSS:') - since + + +def memorypeak(since=0): + '''Return memory usage peak in kilobytes. + ''' + try: + return _VmB('VmPeak:') - since + except: + return float('nan') # old Linux? + + +def residentpeak(since=0): + '''Return resident memory usage peak in kilobytes. + ''' + try: + return _VmB('VmHWM:') - since + except: + return float('nan') # old Linux? + + +def stacksize(since=0): + '''Return stack size in kilobytes. + ''' + return _VmB('VmStk:') - since diff --git a/src/sage/rings/polynomial/pbori/ncf.py b/src/sage/rings/polynomial/pbori/ncf.py new file mode 100644 index 00000000000..0605a99605f --- /dev/null +++ b/src/sage/rings/polynomial/pbori/ncf.py @@ -0,0 +1,41 @@ +from . import Polynomial + + +def get_splitting(f, variables): + ring = s.ring() + s = f.set() + for var_index in variables: + s1 = Polynomial(s.subset1(var_index)) + s0 = Polynomial(s.subset0(var_index)) + f1 = s0 + s1 + f0 = s0 + var = ring.variable(var_index) + if f1.constant(): + return dict(b=f1, a=Polynomial(1, ring), x=var, g=f0) + if f0.constant(): + return dict(b=f0, a=Polynomial(0, ring), x=var, g=f1) + return None + + +def nested_canalyzing_function(f, variables=None): + f = Polynomial(f) + if variables is None: + variables = f.vars_as_monomial() + if not variables.reducible_by(f.vars_as_monomial()): + raise ValueError + if variables != f.vars_as_monomial(): + return False + if len(variables) == 0: + return True + splitting = get_splitting(f, variables) + if splitting is None: + return False + rec = nested_canalyzing_function(splitting["g"], variables / splitting["x" + ]) + if rec: + if rec is True: + return (splitting, ) + else: + return (splitting, rec) + else: + return False diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py new file mode 100644 index 00000000000..fa0ab072807 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -0,0 +1,673 @@ +from __future__ import print_function + +from .PyPolyBoRi import * +from .easy_polynomials import (easy_linear_polynomials as + easy_linear_polynomials_func) +from .statistics import used_vars_set +from random import Random +from warnings import warn +import copy +import os +import sys + + +class GeneratorLimitExceeded(Exception): + """docstring for GeneratorLimitExceeded""" + + def __init__(self, strat): + #super(GeneratorLimitExceeded, self).__init__() + self.strat = strat + +#used_polynomials=list() + +def pkey(p): + return (p[0], len(p)) +mat_counter = 0 + + +def build_and_print_matrices(v, strat): + """old solution using PIL, the currently used implementation is done in C++ + and plots the same matrices, as being calculated""" + treated = BooleSet() + v = list(v) + rows = 0 + polys_in_mat = [] + if len(v) == 0: + return + while(len(v) > 0): + rows = rows + 1 + p = v[0] + v = v[1:] + #used_polynomials.append(p) + for m in list(p.terms()): + m = Monomial(m) + if not m in BooleSet(treated): + i = strat.select(m) + if i >= 0: + p2 = strat[i] + p2 = p2 * (m // p2.lead()) + v.append(p2) + polys_in_mat.append(p) + treated = treated.union(p.set()) + m2i = dict([(v, k) for (k, v) in enumerate(list(Polynomial(BooleSet( + treated)).terms()))]) + #print polys_in_mat + polys_in_mat.sort(key=Polynomial.lead, reverse=True) + polys_in_mat = [[m2i[t] for t in p.terms()] for p in polys_in_mat] + + global mat_counter + mat_counter = mat_counter + 1 + from PIL import Image + from PIL import ImageDraw + + rows = len(polys_in_mat) + cols = len(m2i) + #print cols,rows + im = Image.new("1", (cols, rows), "white") + #im=Image.new("1",(,10000),"white") + for i in range(len(polys_in_mat)): + p = polys_in_mat[i] + for j in p: + assert i < rows + assert j < cols + + im.putpixel((j, i), 0) + + file_name = matrix_prefix + str(mat_counter) + ".png" + if os.path.exists(file_name): + os.remove(file_name) + im.save(file_name) + del im + + print("MATRIX_SIZE:", rows, "x", cols) + + +def multiply_polynomials(l, ring): + """ + >>> r=Ring(1000) + >>> x=r.variable + >>> multiply_polynomials([x(3), x(2)+x(5)*x(6), x(0), x(0)+1], r) + 0 + """ + l = [Polynomial(p) for p in l] + + def sort_key(p): + return p.navigation().value() + l = sorted(l, key=sort_key) + res = Polynomial(ring.one()) + for p in l: + res = p * res + return res + + +def build_and_print_matrices_deg_colored(v, strat): + """old PIL solution using a different color for each degree""" + if len(v) == 0: + return + + treated = BooleSet() + v = list(v) + rows = 0 + polys_in_mat = [] + + while(len(v) > 0): + rows = rows + 1 + p = v[0] + v = v[1:] + for m in list(p.terms()): + m = Monomial(m) + if not m in BooleSet(treated): + i = strat.select(m) + if i >= 0: + p2 = strat[i] + p2 = p2 * (m // p2.lead()) + v.append(p2) + polys_in_mat.append(p) + treated = treated.union(p.set()) + m2i = dict([(v, k) for (k, v) in enumerate(BooleSet(treated))]) + max_deg = max([m.deg() for m in BooleSet(treated)]) + if max_deg == 0: + max_deg = 1 + i2deg = dict([(m2i[m], m.deg()) for m in BooleSet(treated)]) + polys_in_mat = [[m2i[t] for t in p.terms()] for p in polys_in_mat] + polys_in_mat.sort(key=pkey) + global mat_counter + mat_counter = mat_counter + 1 + from PIL import Image + from PIL import ImageDraw + from PIL import ImageColor + + rows = len(polys_in_mat) + cols = len(m2i) + im = Image.new("RGB", (cols, rows), "white") + for i in range(len(polys_in_mat)): + p = polys_in_mat[i] + for j in p: + assert i < rows + assert j < cols + im.putpixel((j, i), ImageColor.getrgb("hsl(" + str(270 - (270 * + i2deg[j]) / max_deg) + ",100%,50%)")) + file_name = matrix_prefix + str(mat_counter) + ".png" + if os.path.exists(file_name): + os.remove(file_name) + im.save(file_name) + del im + + print("MATRIX_SIZE:", rows, "x", cols) + + +def high_probability_polynomials_trick(p, strat): + lead_deg = p.lead_deg() + if lead_deg <= 4: + return + + ring = p.ring() + factor = multiply_polynomials(easy_linear_factors(p), ring) + p = p / factor + + #again, do it twice, it's cheap + lead_deg = p.lead_deg() + if lead_deg <= 3: + return + + if lead_deg > 9: + return + uv = p.vars_as_monomial() + + candidates = [] + + if uv.deg() <= 4: + return + + if not uv.deg() <= lead_deg + 1: + return + + space = uv.divisors() + + lead = p.lead() + for v in lead.variables(): + variable_selection = lead // v + vars_reversed = reversed(list(variable_selection.variables())) + #it's just a way to loop over the cartesian product + for assignment in variable_selection.divisors(): + c_p = assignment + for v in vars_reversed: + if not assignment.reducible_by(v): + c_p = (v + 1) * c_p + + points = (c_p + 1).zeros_in(space) + if p.zeros_in(points).empty(): + candidates.append(c_p * factor) + #there many more combinations depending on plugged in values + for c in candidates: + strat.add_as_you_wish(c) + + +def symmGB_F2_python(G, deg_bound=1000000000000, over_deg_bound=0, + use_faugere=False, use_noro=False, opt_lazy=True, opt_red_tail=True, + max_growth=2.0, step_factor=1.0, implications=False, prot=False, + full_prot=False, selection_size=1000, opt_exchange=True, + opt_allow_recursion=False, ll=False, + opt_linear_algebra_in_last_block=True, max_generators=None, + red_tail_deg_growth=True, matrix_prefix='mat', + modified_linear_algebra=True, draw_matrices=False, + easy_linear_polynomials=True): + if use_noro and use_faugere: + raise ValueError('both use_noro and use_faugere specified') + + def add_to_basis(strat, p): + if p.is_zero(): + if prot: + print("-") + else: + if prot: + if full_prot: + print(p) + print("Result: ", "deg:", p.deg(), "lm: ", p.lead(), "el: ", p + .elength()) + if easy_linear_polynomials and p.lead_deg() > 2: + lin = easy_linear_polynomials_func(p) + for q in lin: + strat.add_generator_delayed(q) + old_len = len(strat) + strat.add_as_you_wish(p) + new_len = len(strat) + if new_len == 1 + old_len: + high_probability_polynomials_trick(p, strat) + + + if prot: + print("#Generators:", len(strat)) + + if (isinstance(G, list)): + if len(G) == 0: + return [] + G = [Polynomial(g) for g in G] + current_ring = G[0].ring() + strat = GroebnerStrategy(current_ring) + strat.reduction_strategy.opt_red_tail = opt_red_tail + strat.opt_lazy = opt_lazy + strat.opt_exchange = opt_exchange + strat.opt_allow_recursion = opt_allow_recursion + strat.enabled_log = prot + strat.reduction_strategy.opt_ll = ll + strat.opt_modified_linear_algebra = modified_linear_algebra + strat.opt_linear_algebra_in_last_block = ( + opt_linear_algebra_in_last_block) + strat.opt_red_by_reduced = False # True + strat.reduction_strategy.opt_red_tail_deg_growth = red_tail_deg_growth + + strat.opt_draw_matrices = draw_matrices + strat.matrix_prefix = matrix_prefix + + for g in G: + if not g.is_zero(): + strat.add_generator_delayed(g) + else: + strat = G + + if prot: + print("added delayed") + i = 0 + try: + while strat.npairs() > 0: + if max_generators and len(strat) > max_generators: + raise GeneratorLimitExceeded(strat) + i = i + 1 + if prot: + print("Current Degree:", strat.top_sugar()) + if (strat.top_sugar() > deg_bound) and (over_deg_bound <= 0): + return strat + if (strat.top_sugar() > deg_bound): + ps = strat.some_spolys_in_next_degree(over_deg_bound) + over_deg_bound -= len(ps) + else: + ps = strat.some_spolys_in_next_degree(selection_size) + + if ps and ps[0].ring().has_degree_order(): + ps = [strat.reduction_strategy.cheap_reductions(p) for p in ps] + ps = [p for p in ps if not p.is_zero()] + if len(ps) > 0: + min_deg = min((p.deg() for p in ps)) + new_ps = [] + for p in ps: + if p.deg() <= min_deg: + new_ps.append(p) + else: + strat.add_generator_delayed(p) + ps = new_ps + + if prot: + print("(", strat.npairs(), ")") + if prot: + print("start reducing") + print("Chain Crit. : ", strat.chain_criterions, "VC:", strat. + variable_chain_criterions, "EASYP", strat. + easy_product_criterions, "EXTP", strat. + extended_product_criterions) + print(len(ps), "spolys added") + + if use_noro or use_faugere: + v = BoolePolynomialVector() + + for p in ps: + if not p.is_zero(): + v.append(p) + if use_noro: + res = strat.noro_step(v) + else: + res = strat.faugere_step_dense(v) + + else: + v = BoolePolynomialVector() + for p in ps: + p = Polynomial( + mod_mon_set( + BooleSet(p.set()), strat.reduction_strategy.monomials)) + if not p.is_zero(): + v.append(p) + if len(v) > 100: + res = parallel_reduce(v, strat, int(step_factor * 10), + max_growth) + else: + if len(v) > 10: + res = parallel_reduce(v, strat, int(step_factor * 30), + max_growth) + else: + res = parallel_reduce(v, strat, int(step_factor * 100 + ), max_growth) + + if prot: + print("end reducing") + + + if len(res) > 0 and res[0].ring().has_degree_order(): + res_min_deg = min([p.deg() for p in res]) + new_res = [] + for p in res: + if p.deg() == res_min_deg: + new_res.append(p) + else: + strat.add_generator_delayed(p) + res = new_res + + def sort_key(p): + return p.lead() + res_cp = sorted(res, key=sort_key) + + + for p in res_cp: + old_len = len(strat) + add_to_basis(strat, p) + if implications and old_len == len(strat) - 1: + strat.implications(len(strat) - 1) + if p.is_one(): + if prot: + print("GB is 1") + return strat + if prot: + print("(", strat.npairs(), ")") + + strat.clean_top_by_chain_criterion() + return strat + except KeyboardInterrupt: + #return strat + raise + + +def GPS(G, vars_start, vars_end): + def step(strat, trace, var, val): + print("plugin: ", var, val) + print("npairs", strat.npairs()) + strat = GroebnerStrategy(strat) + print("npairs", strat.npairs()) + strat.add_generator_delayed(Polynomial(Monomial(Variable(var, strat.r) + ) + val)) + strat = symmGB_F2_python(strat, prot=True, deg_bound=2, + over_deg_bound=10) + if var <= vars_start: + strat = symmGB_F2_python(strat, prot=True, opt_lazy=False, + redTail=False) + if strat.containsOne(): + pass + else: + if var <= vars_start: + #bug: may contain Delayed polynomials + print("!!!!!!! SOLUTION", trace) + raise Exception + #yield trace + else: + branch(strat, trace + [(var, val)], var - 1) + + def branch(strat, trace, var): + while(strat.variableHasValue(var)): + #remember to add value to trace + var = var - 1 + step(strat, trace, var, 0) + step(strat, trace, var, 1) + if G: + strat = GroebnerStrategy(G[0].ring()) + #strat.add_generator(G[0]) + for g in G[:]: + strat.add_generator_delayed(g) + branch(strat, [], vars_end - 1) + + +def GPS_with_proof_path(G, proof_path, deg_bound, over_deg_bound): + def step(strat, trace, proof_path, pos, val): + print(proof_path) + print("plugin: ", pos, val, proof_path[pos]) + print("npairs", strat.npairs()) + strat = GroebnerStrategy(strat) + print("npairs", strat.npairs()) + print("npairs", strat.npairs()) + plug_p = Polynomial(proof_path[pos]) + val + plug_p_lead = plug_p.lead() + if len(plug_p) == 2 and (plug_p + plug_p_lead).deg() == 0: + for v in plug_p_lead: + strat.add_generator_delayed(v + 1) + else: + strat.add_generator_delayed(plug_p) + print("npairs", strat.npairs()) + print("pos:", pos) + strat = symmGB_F2_python(strat, deg_bound=deg_bound, opt_lazy=False, + over_deg_bound=over_deg_bound, prot=True) + print("npairs", strat.npairs()) + pos = pos + 1 + if pos >= len(proof_path): + print("OVER") + strat = symmGB_F2_python(strat, prot=True) + if strat.containsOne(): + pass + else: + if pos >= len(proof_path): + print("npairs", strat.npairs()) + print("minimized:") + for p in strat.minimalize_and_tail_reduce(): + print(p) + #bug: may contain Delayed polynomials + print("!!!!!!! SOLUTION", trace) + raise Exception + #yield trace + else: + branch(strat, trace + [(pos, val)], proof_path, pos) + + def branch(strat, trace, proof_path, pos): + + step(strat, trace, proof_path, pos, 0) + step(strat, trace, proof_path, pos, 1) + strat = GroebnerStrategy(G[0].ring()) + strat.add_generator(Polynomial(G[0])) + for g in G[1:]: + strat.add_generator_delayed(Polynomial(g)) + branch(strat, [], proof_path, 0) + + +def GPS_with_suggestions(G, deg_bound, over_deg_bound, opt_lazy=True, + opt_red_tail=True, initial_bb=True): + def step(strat, trace, var, val): + print(trace) + plug_p = val + var + print("plugin: ", len(trace), plug_p) + trace = trace + [plug_p] + strat = GroebnerStrategy(strat) + + strat.add_generator_delayed(plug_p) + print("npairs", strat.npairs()) + + strat = symmGB_F2_python(strat, deg_bound=deg_bound, + opt_lazy=opt_lazy, over_deg_bound=over_deg_bound, prot=True) + + #pos=pos+1 + if not strat.containsOne(): + branch(strat, trace) + + def branch(strat, trace): + print("branching") + index = strat.suggestPluginVariable() + + if index < 0: + uv = set(used_vars_set(strat)) + lv = set([next(iter(p.lead())).index() for p in strat if p. + lead_deg() == 1]) + candidates = uv.difference(lv) + if len(candidates) > 0: + index = next(iter(candidates)).index() + if index >= 0: + print("chosen index:", index) + step(strat, trace, Polynomial(Monomial(Variable(index))), 0) + step(strat, trace, Polynomial(Monomial(Variable(index))), 1) + else: + print("FINAL!!!", index) + strat = symmGB_F2_python(strat, prot=True) + if not strat.containsOne(): + print("TRACE", trace) + print("SOLUTION") + for p in strat.minimalize_and_tail_reduce(): + print(p) + raise Exception + + def sort_crit(p): + #return (p.deg(),p.lead(),p.elength()) + return (p.lead(), p.deg(), p.elength()) + if not G: + return + strat = GroebnerStrategy(G[0].ring()) + strat.reduction_strategy.opt_red_tail = opt_red_tail # True + strat.opt_exchange = False + strat.opt_allow_recursion = False + #strat.opt_red_tail_deg_growth=False + strat.opt_lazy = opt_lazy + #strat.opt_lazy=True + first_deg_bound = 1 + G = [Polynomial(p) for p in G] + G.sort(key=sort_crit) + higher_deg = {} + if initial_bb: + for g in G: + print(g) + index = strat.select(g.lead()) + if p.deg() == 1: # (index<0): + strat.add_as_you_wish(g) + else: + first_deg_bound = max(first_deg_bound, g.deg()) + strat.add_generator_delayed(g) + print(g, len(strat)) + else: + for g in G: + strat.add_as_you_wish(g) + if initial_bb: + strat = symmGB_F2_python(strat, deg_bound=max(deg_bound, + first_deg_bound), opt_lazy=opt_lazy, over_deg_bound=0, prot=True) + strat.opt_lazy = opt_lazy + print("INITIALIZED") + branch(strat, []) + + +def GPS_with_non_binary_proof_path(G, proof_path, deg_bound, over_deg_bound): + def step(strat, trace, proof_path, pos, choice): + print(proof_path) + print("plugin: ", pos) + print("npairs", strat.npairs()) + strat = GroebnerStrategy(strat) + print("npairs", strat.npairs()) + print("npairs", strat.npairs()) + for p in proof_path[pos][choice]: + print(p) + strat.add_generator_delayed(Polynomial(p)) + + print("npairs", strat.npairs()) + print("pos:", pos) + strat = symmGB_F2_python(strat, deg_bound=deg_bound, + over_deg_bound=over_deg_bound, prot=True) + print("npairs", strat.npairs()) + pos = pos + 1 + if pos >= len(proof_path): + print("OVER") + strat = symmGB_F2_python(strat) + if strat.containsOne(): + pass + else: + if pos >= len(proof_path): + print("npairs", strat.npairs()) + #strat.to_std_out() + l = [p for p in strat] + strat2 = symmGB_F2_python(l) + #strat2.to_std_out() + #bug: may contain Delayed polynomials + print("!!!!!!! SOLUTION", trace) + raise Exception + #yield trace + else: + branch(strat, trace + [(pos, choice)], proof_path, pos) + #workaround because of stack depth + #step(strat,trace+[(var,val)],var-1, 0) + #step(strat,trace+[(var,val)],var-1, 1) + + def branch(strat, trace, proof_path, pos): + for i in range(len(proof_path[pos])): + step(strat, trace, proof_path, pos, i) + + strat = GroebnerStrategy(G[0].ring()) + strat.add_generator(G[0]) + for g in G[1:]: + strat.add_generator_delayed(g) + branch(strat, [], proof_path, 0) + + +def symmGB_F2_C(G, opt_exchange=True, + deg_bound=1000000000000, opt_lazy=False, + over_deg_bound=0, opt_red_tail=True, + max_growth=2.0, step_factor=1.0, + implications=False, prot=False, + full_prot=False, selection_size=1000, + opt_allow_recursion=False, use_noro=False, use_faugere=False, + ll=False, opt_linear_algebra_in_last_block=True, + max_generators=None, red_tail_deg_growth=True, + modified_linear_algebra=True, matrix_prefix="", + draw_matrices=False): + #print implications + if use_noro: + raise NotImplementedError("noro not implemented for symmgb") + if (isinstance(G, list)): + if len(G) == 0: + return [] + + G = [Polynomial(g) for g in G] + strat = GroebnerStrategy(G[0].ring()) + strat.reduction_strategy.opt_red_tail = opt_red_tail + strat.enabled_log = prot + strat.opt_lazy = opt_lazy + strat.opt_exchange = opt_exchange + strat.reduction_strategy.opt_ll = ll + strat.opt_allow_recursion = opt_allow_recursion + strat.opt_linear_algebra_in_last_block = ( + opt_linear_algebra_in_last_block) + strat.enabled_log = prot + strat.opt_modified_linear_algebra = modified_linear_algebra + strat.matrix_prefix = matrix_prefix + strat.opt_draw_matrices = draw_matrices + strat.reduction_strategy.opt_red_tail_deg_growth = red_tail_deg_growth + #strat.add_generator(G[0]) + + strat.redByReduced = False # True + + #if PROT: + # print "added first" + for g in G: # [1:]: + if not g.is_zero(): + strat.add_generator_delayed(g) + strat.symmGB_F2() + return strat + + +def normal_form(poly, ideal, reduced=True): + """ Simple normal form computation of a polynomial against an ideal. + >>> from brial import declare_ring, normal_form + >>> r=declare_ring(['x','y'], globals()) + >>> normal_form(x+y, [y],reduced=True) + x + >>> normal_form(x+y,[x,y]) + 0 + """ + ring = poly.ring() + strat = ReductionStrategy(ring) + strat.opt_red_tail = reduced + ideal = [Polynomial(p) for p in ideal if p != 0] + ideal = sorted(ideal, key=Polynomial.lead) + last = None + for p in ideal: + if p.lead() != last: + strat.add_generator(p) + else: + warn("%s will not used for reductions" % p) + last = p.lead() + return strat.nf(poly) + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py new file mode 100644 index 00000000000..8af6176653c --- /dev/null +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -0,0 +1,288 @@ +# -*- python -*- +# coding=utf-8 + +# parallel.py +# PolyBoRi +# +# Created by Michael Brickenstein on 2008-10-31. +# Copyright 2008 The PolyBoRi Team +# + +from .PyPolyBoRi import if_then_else, CCuddNavigator, BooleSet +from .PyPolyBoRi import (Polynomial, Ring, WeakRingRef, Monomial, + Variable) +from .gbcore import groebner_basis +from zlib import compress, decompress +try: + import copy_reg as copyreg +except ImportError: + import copyreg + + +def to_fast_pickable(l): + """ + to_fast_pickable(l) converts a list of polynomials into a builtin Python value, which is fast pickable and compact. + INPUT: + - a list of Boolean polynomials + OUTPUT: + It is converted to a tuple consisting of + - codes referring to the polynomials + - list of conversions of nodes. + The nodes are sorted, that + n occurs before n.else_branch(), n.then_branch() + Nodes are only listed, if they are not constant. + + A node is converted in this way: + 0 -> 0 + 1 -> 1 + if_then_else(v,t,e) -> (v, index of then branch +2, index of else branch +2) + The shift of +2 is for the constant values implicitly contained in the list. + Each code c refers to the c-2-th position in the conversion list, if c >=2, else to + the corresponding Boolean constant if c in {0, 1} + EXAMPLES: + >>> from brial.PyPolyBoRi import Ring + >>> r=Ring(1000) + >>> x=r.variable + >>> to_fast_pickable([Polynomial(1, r)]) + [[1], []] + >>> to_fast_pickable([Polynomial(0, r)]) + [[0], []] + >>> to_fast_pickable([x(0)]) + [[2], [(0, 1, 0)]] + >>> to_fast_pickable([x(0)*x(1)+x(1)]) + [[2], [(0, 3, 3), (1, 1, 0)]] + >>> to_fast_pickable([x(1)]) + [[2], [(1, 1, 0)]] + >>> to_fast_pickable([x(0)+1]) + [[2], [(0, 1, 1)]] + >>> to_fast_pickable([x(0)*x(1)]) + [[2], [(0, 3, 0), (1, 1, 0)]] + >>> to_fast_pickable([x(0)*x(1)+x(1)]) + [[2], [(0, 3, 3), (1, 1, 0)]] + >>> to_fast_pickable([x(0)*x(1)+x(2)]) + [[2], [(0, 3, 4), (1, 1, 0), (2, 1, 0)]] + >>> p=x(5)*x(23) + x(5)*x(24)*x(59) + x(5) + x(6)*x(23)*x(89) + x(6)*x(60)*x(89) + x(23) + x(24)*x(89) + x(24) + x(60)*x(89) + x(89) + 1 + >>> from_fast_pickable(to_fast_pickable([p]), r)==[p] + True + >>> to_fast_pickable([x(0)*x(1), Polynomial(0, r), Polynomial(1, r), x(3)]) + [[2, 0, 1, 4], [(0, 3, 0), (1, 1, 0), (3, 1, 0)]] + """ + if len(l) == 0: + return [[], []] + + f = l[0] + f = f.set() + r = f.ring() + one = r.one().navigation() + zero = r.zero().navigation() + nodes = set() + + def find_navs(nav): + if not nav in nodes and not nav.constant(): + nodes.add(nav) + find_navs(nav.then_branch()) + find_navs(nav.else_branch()) + for f in l: + f_nav = f.set().navigation() + find_navs(f_nav) + + nodes_sorted = sorted(nodes, key=CCuddNavigator.value) + nodes2i = {one: 1, zero: 0} + for (i, n) in enumerate(nodes_sorted): + nodes2i[n] = i + 2 + + for i in range(len(nodes_sorted)): + n = nodes_sorted[i] + t = nodes2i[n.then_branch()] + e = nodes2i[n.else_branch()] + nodes_sorted[i] = (n.value(), t, e) + + return [[nodes2i[f.set().navigation()] for f in l], nodes_sorted] + + +def from_fast_pickable(l, r): + """from_fast_pickable(l, ring) undoes the operation to_fast_pickable. The first argument is an object created by to_fast_pickable. + For the specified format, see the documentation of to_fast_pickable. + The second argument is ring, in which this polynomial should be created. + INPUT: + see OUTPUT of to_fast_pickable + OUTPUT: + a list of Boolean polynomials + EXAMPLES: + >>> from brial.PyPolyBoRi import Ring + >>> r=Ring(1000) + >>> x = r.variable + >>> from_fast_pickable([[1], []], r) + [1] + >>> from_fast_pickable([[0], []], r) + [0] + >>> from_fast_pickable([[2], [(0, 1, 0)]], r) + [x(0)] + >>> from_fast_pickable([[2], [(1, 1, 0)]], r) + [x(1)] + >>> from_fast_pickable([[2], [(0, 1, 1)]], r) + [x(0) + 1] + >>> from_fast_pickable([[2], [(0, 3, 0), (1, 1, 0)]], r) + [x(0)*x(1)] + >>> from_fast_pickable([[2], [(0, 3, 3), (1, 1, 0)]], r) + [x(0)*x(1) + x(1)] + >>> from_fast_pickable([[2], [(0, 3, 4), (1, 1, 0), (2, 1, 0)]], r) + [x(0)*x(1) + x(2)] + >>> from_fast_pickable([[2, 0, 1, 4], [(0, 3, 0), (1, 1, 0), (3, 1, 0)]], r) + [x(0)*x(1), 0, 1, x(3)] + """ + i2poly = {0: r.zero(), 1: r.one()} + (indices, terms) = l + + for i in reversed(range(len(terms))): + (v, t, e) = terms[i] + t = i2poly[t] + e = i2poly[e] + terms[i] = if_then_else(v, t, e) + i2poly[i + 2] = terms[i] + return [Polynomial(i2poly[i]) for i in indices] + + +def _calculate_gb_with_keywords(args): + (I, kwds_as_single_arg) = args + import traceback + try: + return groebner_basis(I, **kwds_as_single_arg) + except: + raise ValueError(traceback.format_exc()) + + +def _decode_polynomial(code): + return from_fast_pickable(*code)[0] + + +def _encode_polynomial(poly): + return (to_fast_pickable([poly]), poly.ring()) + + +def pickle_polynomial(self): + return (_decode_polynomial, (_encode_polynomial(self), )) + +copyreg.pickle(Polynomial, pickle_polynomial) + + +def pickle_bset(self): + return (BooleSet, (Polynomial(self), )) + +copyreg.pickle(BooleSet, pickle_bset) + + +def pickle_monom(self): + return (Monomial, ([var for var in self.variables()], )) + +copyreg.pickle(Monomial, pickle_monom) + + +def pickle_var(self): + return (Variable, (self.index(), self.ring())) + +copyreg.pickle(Variable, pickle_var) + + +def _decode_ring(code): + import os + (identifier, data, varnames, blocks) = code + + global _polybori_parallel_rings + try: + _polybori_parallel_rings + except NameError: + _polybori_parallel_rings = dict() + + for key in [key for key in _polybori_parallel_rings + if not _polybori_parallel_rings[key][0]()]: + del _polybori_parallel_rings[key] + + if identifier in _polybori_parallel_rings: + ring = _polybori_parallel_rings[identifier][0]() + else: + ring = None + + if not ring: + varnames = decompress(varnames).split('\n') + (nvars, ordercode) = data + ring = Ring(nvars, ordercode, names=varnames, blocks=blocks) + storage_data = (WeakRingRef(ring), code) + _polybori_parallel_rings[identifier] = storage_data + _polybori_parallel_rings[(ring.id(), os.getpid())] = storage_data + + return ring + + +def _encode_ring(ring): + import os + identifier = (ring.id(), os.getpid()) + + global _polybori_parallel_rings + try: + _polybori_parallel_rings + except NameError: + _polybori_parallel_rings = dict() + + for key in [key for key in _polybori_parallel_rings + if not _polybori_parallel_rings[key][0]()]: + del _polybori_parallel_rings[key] + + if identifier in _polybori_parallel_rings: + code = _polybori_parallel_rings[identifier][1] + else: + nvars = ring.n_variables() + data = (nvars, ring.get_order_code()) + varnames = '\n'.join([str(ring.variable(idx)) for idx in range(nvars) + ]) + blocks = list(ring.blocks()) + code = (identifier, data, compress(varnames), blocks[:-1]) + _polybori_parallel_rings[identifier] = (WeakRingRef(ring), code) + + return code + + +def pickle_ring(self): + return (_decode_ring, (_encode_ring(self), )) + +copyreg.pickle(Ring, pickle_ring) + + +def groebner_basis_first_finished(I, *l): + """ + INPUT: + - I ideal + - l: keyword dictionaries, which will be keyword arguments to groebner_basis. + OUTPUT: + - tries to compute groebner_basis(I, **kwd) for kwd in l + - returns the result of the first terminated computation + EXAMPLES: + >>> from brial.PyPolyBoRi import Ring + >>> r=Ring(1000) + >>> ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] + >>> #groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) + [x(1), x(2)] + """ + if not I: + return [] + try: + from multiprocessing import Pool + except: + from processing import Pool + + pool = Pool(processes=len(l)) + it = pool.imap_unordered(_calculate_gb_with_keywords, + [(I, kwds) for kwds in l]) + res = next(it) + + pool.terminate() + + return res + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/parsegat.py b/src/sage/rings/polynomial/pbori/parsegat.py new file mode 100644 index 00000000000..b71eb3b3955 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/parsegat.py @@ -0,0 +1,524 @@ +from __future__ import print_function +#import pathadjuster + +if __name__ == '__main__': + from sys import path as search_path + from os import path as file_path + search_path.append(file_path.join(file_path.dirname(__file__), '..')) + + +def _exists(): + """PolyBoRi convention: checking optional components for prerequisites here + + >>> _exists() + True + """ + try: + import pyparsing + except ImportError: + return False + + return True + +#from .gbrefs import declare_ring +from . import * +from .ll import ll_encode +from optparse import OptionParser +from .statistics import used_vars_set +from itertools import chain +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO +from sys import stderr +parser = OptionParser() + +gat_max = 20000 +next_max = 2000 +output_max = 2000 +inter_max = 20000 +input_max = 2000 +state_max = 2000 + +# declare_ring([Block("x",gat_max),Block("xnext",next_max,reverse = +# True),Block("xoutput",output_max,reverse = +# True),Block("xinter",inter_max,reverse = +# True),Block("xinput",input_max,reverse = +# True),Block("xstate",state_max,reverse = True)],globals()) input order is +# antitopological we reverse, when mapping to final variables +parser.add_option("--forward-iteration", + action="store_true", dest="forward", default=True, + help="switch between forward and backward iteration") + +parser.add_option("--backward-iteration", + action="store_false", dest="forward", default=True, + help="switch between forward and backward iteration") + +parser.add_option("--include-outputs", + action="store_true", dest="include_outputs", default=True, + help="include circuit outputs") + +parser.add_option("--initialize-state", action="store", dest="initialize", + default="noinit", type="choice", choices=["noinit", "random", "zero"], + help="initialize state variables/only useful with forward iteration") + + +def parse_identifier(str, log, tokens): + return int(tokens[0]) + + +def throw_away(str, log, tokens): + return "None" + +assigned = set() +from random import Random + + +def add_negated(str, log, tokens): + p = tokens[1] + l = p.lead() + if not l in assigned and r.randint(0, 200) == 0: + assigned.add(l) + print("NEG", p + 1) + return p + 1 + else: + return "NONE" + + +class FamiliarityException(Exception): + """docstring for FamiliarityException""" + + def __init__(self): + super(FamiliarityException, self).__init__() + + +def fix_symbol_name(str, log, tokens): + return (tokens[0].replace("-", "_").replace("]", "").replace("[", ""). + replace("/", "_")) + + +class DeterminingEquation(object): + """docstring for DeterminingEquation""" + + def __init__(self, variable, mapped_to): + super(DeterminingEquation, self).__init__() + self.variable = variable + self.mapped_to = mapped_to + + def _get_equation(self): + return self.variable + self.mapped_to + equation = property(_get_equation) + + +# be careful: for next state/output we directly generate mapped_to + value +# instead of introducing a variable and mapping it later +class VariableManager(object): + """docstring for VariableManager""" + + def __init__(self, ring, prefix="", initialize="noinit", **kwd): + super(VariableManager, self).__init__() + self.ring = ring + self.output = [] + self.input = [] + self.state = [] + self.next = [] + self.deletion_candidates = [] + self.intermediate = [] + + self.next_state_equations = [] + self.intermediate_equations = [] + self.__map = dict() + self.prefix = prefix + self.initialize = initialize + self.ands = dict() + self.xors = dict() + self.tails = [] + for (k, v) in kwd.items(): + setattr(self, k, v) + + def gauss(self, determining_equations): + l = [] + res = [] + output_set = set(self.output) + for p in determining_equations: + eq = p.equation + if not p.variable in output_set and eq.deg() == 1: + assert eq.lead() == p.variable + l.append(eq) + self.deletion_candidates.append(p.variable) + else: + res.append(p) + l = gauss_on_polys(l) + for p in l: + res.append(DeterminingEquation(p.lead(), p + p.lead())) + return res + + def ideals(self): + def sort_key(p): + return p.navigation().value() + + def my_sort(l): + return sorted(l, key=sort_key) + #self.intermediate_equations = self.gauss(self.intermediate_equations) + tail_variables = set(used_vars_set([e.mapped_to for e in chain(self. + next_state_equations, self.intermediate_equations)]).variables()) + to_delete = [] + for v in self.deletion_candidates: + if not v in tail_variables: + to_delete.append(v) + to_delete = set(to_delete) + self.intermediate_equations = [e for e in self.intermediate_equations + if not e.variable in to_delete] + + # we don't delete the variable itself, as we will confuse + # gluemultipliers,that looks on the lengths of self.intermediate... + def equations(determining_equations): + return [e.equation for e in determining_equations] + ideal_state = [] + ideal_next_state = equations(self.next_state_equations) + ideal_intermediate = equations(self.intermediate_equations) + + if self.initialize != "noinit": + if self.initialize == "zero": + initializer = zero_fun + else: + initializer = random_bit + for v in variables: + if str(v)[:1] == "s": + ideal_state.append(v + initializer()) + + return [ + my_sort(self.apply_map(i)) for i in + (ideal_state, ideal_intermediate, ideal_next_state)] + + def set_variable_name(self, i, name): + _set_variable_name(self.ring, i, self.prefix + name) + + def parse_output_action(self, str, log, tokens): + p = Polynomial(tokens[1]) + #self.output.append(p) + #mapped_to = xoutput(len(self.output)-1) + mapped_to = self.xoutput(len(self.output)) + self.output.append(mapped_to) + self.set_variable_name(mapped_to.index(), "out_" + tokens[2]) + self.tails.append(p) + if self.include_outputs: + self.intermediate_equations.append(DeterminingEquation(mapped_to, + p)) + return mapped_to + p + else: + return "NONE" + + def parse_buffer_action(self, str, log, tokens): + return "NONE" + + def parse_next_state_action(self, str, log, tokens): + p = Polynomial(tokens[1]) + #self.next.append(p) + #mapped_to = xnext(len(self.next)-1) + mapped_to = self.xnext(len(self.next)) + self.next.append(mapped_to) + self.set_variable_name(mapped_to.index(), "nstate_" + tokens[2]) + self.next_state_equations.append(DeterminingEquation(mapped_to, p)) + self.tails.append(p) + return mapped_to + p + + def parse_state_action(self, str, log, tokens): + p = self.x(tokens[0]) + #self.state.append(p) + #mapped_to = xstate(len(self.state)-1) + mapped_to = self.xstate(len(self.state)) + self.state.append(mapped_to) + self.set_variable_name(mapped_to.index(), "state_" + tokens[2]) + self.map(p, mapped_to) + return "NONE" + + def parse_input_action(self, str, log, tokens): + p = self.x(tokens[0]) + #self.input.append(p) + #mapped_to = xinput(len(self.input)-1) + mapped_to = self.xinput(len(self.input)) + self.input.append(mapped_to) + self.set_variable_name(mapped_to.index(), "in_" + tokens[2]) + self.map(p, mapped_to) + return "NONE" + + def evaluates_to_variables(self, signal): + ands = self.ands[signal] + return list( + used_vars_set(ands).variables()) + + def eval_and_gat(self, lit, inputs=None): + if inputs is None: + inputs = self.ands[lit.lex_lead()] + assert len(inputs) > 0 + res = inputs[0] + for i in inputs[1:]: + res = res * i + if lit.has_constant_part(): + res = res + 1 + return res + + def simplified_product(self, out, op1, op2): + + if op1.deg() == 1 and op2.deg() == 1: + op1v = op1.lex_lead() + op2v = op2.lex_lead() + + if op1v not in self.xors \ + and op2v not in self.xors \ + and op1v in self.ands \ + and op2v in self.ands: + mapped_to = [p for p in chain(self.ands[op1v], self.ands[op2v])] + for p in mapped_to: + if p.deg() != 1: + return op1 * op2 + mapped_to = set([p.lex_lead() for p in mapped_to]) + #print >>stderr, op1, op2, mapped_to + if len(mapped_to) <= 2: + #xor pattern + self.deletion_candidates.extend([op1v, op2v]) + assert op1.vars_as_monomial().deg() == 1 + assert op2.vars_as_monomial().deg() == 1 + op1 = self.eval_and_gat(op1) + op2 = self.eval_and_gat(op2) + outer_res = self.eval_and_gat(out, [op1, op2]) + self.xors[out] = outer_res + else: + try: + for (op1, op1v, op2, op2v) in \ + [(op1, op1v, op2, op2v), (op2, op2v, op1, op1v)]: + (op1e, op2e) = [ + self.evaluates_to_variables(left_parent) + for left_parent in (op1v, op2v)] + for (i, s) in enumerate(op2e): + if s in self.xors or s not in self.ands: + continue + + if self.evaluates_to_variables(s) == op1e: + raise FamiliarityException() + except FamiliarityException: + op1 = self.eval_and_gat(op1) + op2_inputs = list(self.ands[op2v]) + for (i, s2) in enumerate(op2_inputs): + if s2.lex_lead() == s: + op2_inputs[i] = self.eval_and_gat(s2) + op2 = self.eval_and_gat(op2, inputs=op2_inputs) + self.deletion_candidates.extend((s, op1v, op2v)) + else: + # pattern + # 34 AND ~16 ~15 + # 35 AND 14 34 + # 36 AND 16 15 + # 37 AND ~14 36 + # 38 AND ~37 ~35 + + try: + (op1a, op2a) = [self.ands.get(v) + for v in (op1v, op2v)] + assert len(op1a) == 2 + assert len(op2a) == 2 + print("+", file=stderr) + for op1a in [[op1a[0], op1a[1]], op1a]: + for op2a in [[op2a[0], op2a[1]], op2a]: + print(op1a[0], op2a[0], file=stderr) + if op1a[0] == op2a[0] + 1: + print("-", file=stderr) + if op1a[1] in self.ands and \ + op2a[1] in self.ands and \ + op1a[1] not in self.xors \ + and op2a[1] not in self.xors: + if set(self.ands[op1a[1]]) == set([1 + + s for s in self.ands[op2a[1]]]): + raise FamiliarityException + except FamiliarityException: + self.deletion_candidates.extend([op1v, op2v, op2a[1]. + lex_lead(), op1a[1].lex_lead()]) + for group in (op2a, op1a): + group[1] = self.eval_and_gat(group[1]) + (op1, op2) = [ + self.eval_and_gat(op, inputs) for (op, inputs) + in [(op1, op1a), (op2, op2a)]] + + return op1 * op2 + + def parse_and_action(self, string, log, tokens): + + inter = self.x(tokens[0]) + op1 = tokens[2] + op2 = tokens[3] + #self.intermediate.append(inter) + #mapped_to = xinter(len(self.intermediate)-1) + mapped_to = self.xinter(len(self.intermediate)) + self.ands[inter] = (op1, op2) + self.intermediate.append(mapped_to) + self.set_variable_name(mapped_to.index(), "y" + str(tokens[0])) + self.map(inter, mapped_to) + tail = self.simplified_product(inter, op1, op2) + self.tails.append(tail) + eq = inter + tail + self.intermediate_equations.append(DeterminingEquation(inter, tail)) + return eq + + def map(self, from_, to): + self.__map[from_] = to + + def apply_map(self, eq): + encoded = ll_encode((k + v for (k, v) in self.__map.items())) + return [ll_red_nf_noredsb(p, encoded) for p in eq] + + def parse_sign(self, str, log, tokens): + if list(tokens) != [0]: + return [1] + else: + return tokens + + def parse_idenfifier_ref(self, str, log, tokens): + if tokens[1] in (0, 1): + #from sys import stderr + #stderr.write("TOKENS:"+repr(tokens)) + #stderr.flush() + return Polynomial(tokens[0] + tokens[1]) + return tokens[0] + self.x(tokens[1]) + + +def generate_gat_bnf(manager): + + from pyparsing import (Literal, CaselessLiteral, Word, Combine, Group, + Optional, ZeroOrMore, Forward, nums, alphas, Or, restOfLine, + OneOrMore, restOfLine, alphanums) + + identifier = Word(nums).setParseAction(parse_identifier) + symbolic_name = (Word(alphanums + "_-[]/", alphanums + "_-[]/")). \ + setParseAction(fix_symbol_name) + meta = ZeroOrMore( + Or( + [ + CaselessLiteral("WRITER"), + CaselessLiteral("DESIGN"), + CaselessLiteral("ORIGIN")]) + restOfLine) + end = CaselessLiteral("END") + and_ = CaselessLiteral("AND") + input_ = CaselessLiteral("INPUT") + state = CaselessLiteral("STATE") + nstate = CaselessLiteral("NSTATE") + output = CaselessLiteral("OUTPUT") + buffer_ = CaselessLiteral("BUFFER") + identifier_ref = \ + (Optional(Literal("~"), default=0).setParseAction( + manager.parse_sign) + identifier).setParseAction( + manager.parse_idenfifier_ref) + + input_line = (identifier + input_ + symbolic_name).setParseAction(manager. + parse_input_action) + and_line = (identifier + and_ + identifier_ref + identifier_ref). \ + setParseAction(manager.parse_and_action) + buffer_line = (buffer_ + identifier_ref + symbolic_name).setParseAction( + manager.parse_buffer_action) + output_line = (output + identifier_ref + symbolic_name).setParseAction( + manager.parse_output_action) + state_line = (identifier + state + symbolic_name).setParseAction(manager. \ + parse_state_action) + nstate_line = (nstate + identifier_ref + symbolic_name).setParseAction( + manager.parse_next_state_action) + assignment = Or([output_line, and_line, input_line, state_line, + nstate_line, buffer_line]) + + gat = meta + OneOrMore(assignment) + end + return gat +generator = Random(123) + + +def parse(f, manager): + f = open(f) + content = f.read() + bnf = generate_gat_bnf(manager) + parsed = bnf.parseString(content) + f.close() + + +def zero_fun(): + return 0 + + +def random_bit(): + return generator.randint(0, 1) + + +def format_grouped(l, group_size=10, indent=0): + s = StringIO() + s.write("[") + last = len(l) - 1 + for (i, e) in enumerate(l): + if i % group_size == 0: + s.write("\n" + indent * " ") + s.write(e) + if i != last: + s.write(", ") + s.write("]") + return s.getvalue() + + +def generate_three_ideal_output(ideal_state, ideal_intermediate, + ideal_next_state, variables): + print("declare_ring(" + format_grouped([repr(str(v)) for v in variables], + indent=4) + ")") + print("block_start_hints=" + repr(block_starts)) + print("ideal_intermediate=[") + print(",\n".join((str(p) for p in ideal_intermediate))) + print("]") + print("ideal_state=[") + print(",\n".join((str(p) for p in ideal_state))) + print("]") + print("ideal_next_state=[") + print(",\n".join((str(p) for p in ideal_next_state))) + print("]") + print("ideal = ideal_state+ideal_next_state+ideal_intermediate") + +if __name__ == '__main__': + (options, args) = parser.parse_args() + kwd_args = dict() + ring = declare_ring([ + "t", + Block("x", gat_max, reverse=True), + Block("xnext", next_max, reverse=True), + Block("xoutput", output_max, reverse=True), + Block("xinter", inter_max, reverse=True), + Block("xinput", input_max, reverse=True), + Block("xstate", state_max, reverse=True)], kwd_args) + + manager = VariableManager(ring=ring, include_outputs=options. + include_outputs, initialize=options.initialize, **kwd_args) + + from sys import argv + f = args[0] + + parse(f, manager) + (ideal_state, ideal_intermediate, ideal_next_state) = manager.ideals() + ideal = ideal_state + ideal_intermediate + ideal_next_state + + variables = [] + used_vars = set(used_vars_set(ideal).variables()) + if not options.forward: + variables = list(chain(reversed(manager.next), reversed(manager.output + ), reversed(manager.intermediate), reversed(manager.input), + reversed(manager.state))) + else: + variables = list( + chain(reversed(manager.output), + reversed(manager.intermediate), + reversed(manager.input), + reversed(manager.state), + reversed(manager.next))) + variables = ["t"] + variables + beginnings = [str(v)[:1] for v in variables] + block_starts = [] + last = beginnings[0] + + for (i, s) in enumerate(beginnings): + if s != last: + block_starts.append(i) + last = s + + generate_three_ideal_output(ideal_state, ideal_intermediate, + ideal_next_state, variables) diff --git a/src/sage/rings/polynomial/pbori/partial.py b/src/sage/rings/polynomial/pbori/partial.py new file mode 100644 index 00000000000..ffb75e4870d --- /dev/null +++ b/src/sage/rings/polynomial/pbori/partial.py @@ -0,0 +1,49 @@ +from . import BooleSet, interpolate_smallest_lex + + +class PartialFunction(object): + """docstring for PartialFunction""" + + def __init__(self, zeros, ones): + super(PartialFunction, self).__init__() + self.zeros = zeros.set() + self.ones = ones.set() + + def interpolate_smallest_lex(self): + return interpolate_smallest_lex(self.zeros, self.ones) + + def __str__(self): + return "PartialFunction(zeros=" + str(self.zeros) + ", ones=" + str( + self.ones) + ")" + + def definedOn(self): + return self.zeros.union(self.ones) + + def __add__(self, other): + domain = self.definedOn().intersect(other.definedOn()) + zeros = self.zeros.intersect(other.zeros).union(self.ones.intersect( + other.ones)) + ones = self.zeros.intersect(other.ones).union(self.ones.intersect( + other.zeros)) + assert zeros.diff(domain).empty() + assert ones.diff(domain).empty() + return PartialFunction(zeros, ones) + + def __repr__(self): + return str(self) + + def __mul__(self, other): + zeros = self.zeros.union(other.zeros) + ones = self.ones.intersect(other.ones) + return PartialFunction(zeros, ones) + + def __or__(self, other): + zeros = self.zeros.intersect(other.zeros) + ones = self.ones.union(other.ones) + return PartialFunction(zeros, ones) + + def __xor__(self, other): + return self + other + + def __and__(self, other): + return self * other diff --git a/src/sage/rings/polynomial/pbori.pxd b/src/sage/rings/polynomial/pbori/pbori.pxd similarity index 100% rename from src/sage/rings/polynomial/pbori.pxd rename to src/sage/rings/polynomial/pbori/pbori.pxd diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx similarity index 100% rename from src/sage/rings/polynomial/pbori.pyx rename to src/sage/rings/polynomial/pbori/pbori.pyx diff --git a/src/sage/rings/polynomial/pbori/plot.py b/src/sage/rings/polynomial/pbori/plot.py new file mode 100644 index 00000000000..9364d2bcbe5 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/plot.py @@ -0,0 +1,256 @@ +# -*- python -*- +# encoding: utf-8 +""" +plot.py + +Created by Michael Brickenstein on 2008-10-17. +Copyright (c) 2008 The PolyBoRi Team. + + +""" + + +def _exists(): + """PolyBoRi convention: checking optional components for prerequisites here + + >>> _exists() + True + """ + try: + import jinja2 + except ImportError: + try: + import jinja + except ImportError: + return False + + try: + from subprocess import Popen, PIPE + from os import devnull + out = open(devnull) + process = Popen(["dot", "-V"], stderr=out, stdout=out) + out.close() + except: + return False + + return True + +import sys +import os +from .PyPolyBoRi import Ring, Polynomial, BooleSet +from subprocess import Popen, PIPE + +graph_template = """ +digraph polynomial{ +graph [ +ordering="out" +#if highlight_monomial +, label = "${display_monomial(highlight_monomial)}" +#end +, fontsize=${fontsize} +]; + +#for n in nodes +${identifier(n)}[label="${label(n)}", shape="${shape(n)}"]; +#end + +#for n in non_constant_nodes +${identifier(n)} -> ${identifier(n.else_branch())} [style="dashed", color="${color_else}", arrowhead="vee", penwidth="${penwidth_else(n)}"]; +${identifier(n)} -> ${identifier(n.then_branch())} [color="${color_then}", arrowhead="vee", penwidth="${penwidth_then(n)}"]; +#end +} +""" + +graph_template_jinja = """ +digraph polynomial{ +{% if landscape %} +rankdir=LR; +{% endif %} +graph [ ordering="out" +{% if highlight_monomial %} +, label = "{{display_monomial(highlight_monomial)}}" +{% endif %} +, fontsize={{fontsize}} +]; + +{% for n in nodes %} +{{identifier(n)}}[label="{{label(n)}}", shape="{{shape(n)}}"]; +{% endfor %} + +{% for n in non_constant_nodes %} +{{identifier(n)}} -> {{identifier(n.else_branch())}} [style="dashed", color="{{color_else}}", arrowhead="vee", penwidth="{{penwidth_else(n)}}"]; +{{identifier(n)}} -> {{identifier(n.then_branch())}} [color="{{color_then}}", arrowhead="vee", penwidth="{{penwidth_then(n)}}"]; +{% endfor %} +} +""" + +ELSE = "else" +THEN = "then" + + +def render_genshi(data_dict): + from genshi.template import TextTemplate + tmpl = TextTemplate(graph_template) + stream = tmpl.generate(**data_dict) + return str(stream) + + +def render_jinja(data_dict): + try: + from jinja2 import Environment + except: + from jinja import Environment + env = Environment() + tmpl = env.from_string(graph_template_jinja) + return tmpl.render(**data_dict) + + +def monomial_path_in_zdd(mon, graph): + res = [] + if not mon in BooleSet(graph): + raise ValueError + graph_nav = BooleSet(graph).navigation() + mon_nav = BooleSet(mon).navigation() + while not mon_nav.constant(): + while graph_nav.value() < mon_nav.value(): + res.append((graph_nav, ELSE)) + graph_nav = graph_nav.else_branch() + assert mon_nav.value() == graph_nav.value() + res.append((graph_nav, THEN)) + mon_nav = mon_nav.then_branch() + graph_nav = graph_nav.then_branch() + while not graph_nav.constant(): + res.append((graph_nav, ELSE)) + graph_nav = graph_nav.else_branch() + return dict(res) + + +def plot(p, filename, colored=True, format="png", + highlight_monomial=None, fontsize=14, + template_engine='jinja', landscape=False + ): + """plots ZDD structure to in format + + EXAMPLES: + + >>> r=Ring(1000) + >>> x = r.variable + >>> plot(x(1)+x(0),"/dev/null", colored=True) + >>> plot(x(1)+x(0),"/dev/null", colored=False) + """ + THICK_PEN = 5 + highlight_path = dict() + if highlight_monomial: + highlight_path = monomial_path_in_zdd(highlight_monomial, p) + + def display_monomial(m): + try: + return unicode(m).replace(u"*", u".") + except NameError: + return str(m).replace("*", "⋅") + + def penwidth_else(n): + if n in highlight_path and highlight_path[n] == ELSE: + return THICK_PEN + return 1 + + def penwidth_then(n): + if n in highlight_path and highlight_path[n] == THEN: + return THICK_PEN + return 1 + if not colored: + color_then = "black" + color_else = "black" + else: + color_then = "red" + color_else = "blue" + + def find_navs(nav): + if not nav in nodes: + nodes.add(nav) + if not nav.constant(): + find_navs(nav.then_branch()) + find_navs(nav.else_branch()) + p = Polynomial(p) + nodes = set() + nav = p.navigation() + find_navs(nav) + non_constant_nodes = [n for n in nodes if not n.constant()] + node_to_int = dict([(n, i) for (i, n) in enumerate(nodes)]) + + r = p.ring() + + def identifier(n): + return "n" + str(node_to_int[n]) + + def label(n): + if n.constant(): + if n.terminal_one(): + return "1" + else: + return "0" + else: + return str(r.variable(n.value())) + + def shape(n): + if n.constant(): + return "box" + else: + return "ellipse" + renderers = dict(genshi=render_genshi, jinja=render_jinja) + + dot_input = renderers[template_engine](locals()) + if not isinstance(dot_input, bytes): + dot_input = dot_input.encode('utf-8') + process = Popen(["dot", "-T" + format, "-o", filename], stdin=PIPE, + stdout=PIPE) + + process.stdin.write(dot_input) + process.stdin.close() + process.wait() + + +def main(): + r = Ring(1000) + x = Variable = VariableFactory(r) + from os import system + from .specialsets import all_monomials_of_degree_d, power_set + full_set = list(power_set([Variable(i) for i in range(15)])) + from random import Random + generator = Random(123) + random_set = sum(generator.sample(full_set, 30)) + full_polynomial = list(all_monomials_of_degree_d(3, [Variable(i) for i in + range(30)])) + random_poly = sum(generator.sample(full_polynomial, 30)) + polynomials = [ + x(1) * x(2) + x(3), + (x(1) + 1) * (x(2) + x(3)), + (x(1) + 1) * (x(2) + 1) * (x(3) + 1), + x(1) * x(2) + x(2) * x(3) + x(1) * x(3) + x(1), + x(0) + x(1) + x(2) + x(3) + x(4) + x(5), + all_monomials_of_degree_d(3, [x(i) for i in range(10)]), + power_set([x(i) for i in range(10)]), + random_poly, + random_set, + Polynomial(all_monomials_of_degree_d(3, [x(i) for i in range(10)])) + + Polynomial(power_set([x(i) for i in range(10)])), + Polynomial(power_set([x(i) for i in range(10)])) + 1 + ] + for colored in [True, False]: + if colored: + colored_suffix = "_colored" + else: + colored_suffix = "" + for format in ["png", "svg"]: + for (i, p) in enumerate(polynomials): + + #dot_file=str(i) +colored_suffix+".dot" + #f=open(dot_file, "w") + #f.write(dot) + #f.close() + out_file = str(i) + colored_suffix + "." + format + plot(p, out_file, colored=colored, format=format) + #system("dot -Tpng -o "+png_file+" " + dot_file) + +if __name__ == '__main__': + main() diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py new file mode 100644 index 00000000000..5bf74060cac --- /dev/null +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -0,0 +1,92 @@ +from .PyPolyBoRi import Monomial, random_set, Polynomial, \ +set_random_seed, Ring, ll_red_nf_redsb, Variable +from .ll import ll_encode +from random import Random +from pprint import pprint, pformat +from .blocks import declare_ring + + +def gen_random_poly(ring, l, deg, vars_set, seed=123): + + myrange = vars_set + r = Random(seed) + + def helper(samples): + if samples == 0: + return Polynomial(ring.zero()) + if samples == 1: + d = r.randint(0, deg) + variables = r.sample(myrange, d) + m = Monomial(ring) + for v in sorted(set(variables), reverse=True): + m = m * Variable(v, ring) + return Polynomial(m) + assert samples >= 2 + return helper(samples / 2) + helper(samples - samples / 2) + p = Polynomial(ring.zero()) + while(len(p) < l): + p = Polynomial(p.set().union(helper(l - len(p)).set())) + return p + + +def sparse_random_system(ring, number_of_polynomials, + variables_per_polynomial, degree, random_seed=None): + """ + generates a system, which is sparse in the sense, that each polynomial + contains only a small subset of variables. In each variable that occurrs in a polynomial it is dense in the terms up to the given degree (every term occurs with probability 1/2). + The system will be satisfiable by at least one solution. + >>> from brial import * + >>> r=Ring(10) + >>> s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) + >>> [p.deg() for p in s] + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + >>> sorted(groebner_basis(s), reverse=True) + [x(0), x(1), x(2), x(3), x(4) + 1, x(5), x(6) + 1, x(7), x(8) + 1, x(9)] + """ + if random_seed is not None: + set_random_seed(random_seed) + random_generator = Random(random_seed) + solutions = [] + variables = [ring.variable(i) for i in range(ring.n_variables())] + for v in variables: + solutions.append(v + random_generator.randint(0, 1)) + solutions = ll_encode(solutions) + res = [] + while len(res) < number_of_polynomials: + variables_as_monomial = Monomial( + random_generator.sample( + variables, + variables_per_polynomial) + ) + p = Polynomial(random_set(variables_as_monomial, 2 ** ( + variables_per_polynomial - 1))) + p = sum([p.graded_part(i) for i in range(degree + 1)]) + if p.deg() == degree: + res.append(p) + res = [p + ll_red_nf_redsb(p, solutions) for p in res] + # evaluate it to guarantee a solution + return res + + +def sparse_random_system_data_file_content( + number_of_variables, **kwds): + r""" + >>> sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) # doctest: +ELLIPSIS + "declare_ring(['x'+str(i) for in xrange(10)])\nideal=\\\n[...]\n\n" + """ + dummy_dict = dict() + r = declare_ring(['x' + str(i) for i in range(number_of_variables)], + dummy_dict) + polynomials = sparse_random_system(r, **kwds) + polynomials = pformat(polynomials) + res = "declare_ring(['x'+str(i) for in xrange(%s)])\nideal=\\\n%s\n\n" % ( + number_of_variables, polynomials) + return res + + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/src/sage/rings/polynomial/pbori/rank.py b/src/sage/rings/polynomial/pbori/rank.py new file mode 100644 index 00000000000..d41aeab3e5b --- /dev/null +++ b/src/sage/rings/polynomial/pbori/rank.py @@ -0,0 +1,26 @@ +def input_signals(p): + return list((p + p.lex_lead()).vars_as_monomial().variables()) + + +def output_signal(p): + return next(iter(p.lex_lead().variables())) + + +def rank(data): + parents = dict() + res = dict() + for p in data: + # print p, output_signal(p) + out = output_signal(p) + parents.setdefault(out, []) + for v in input_signals(p): + parents.setdefault(v, []).append(out) + + def do_rank(v): + if v in res: + return res[v] + my_res = res[v] = max([do_rank(p) + 1 for p in parents[v]] + [0]) + return my_res + for v in parents.keys(): + do_rank(v) + return res diff --git a/src/sage/rings/polynomial/pbori/simplebb.py b/src/sage/rings/polynomial/pbori/simplebb.py new file mode 100644 index 00000000000..e9faf187a94 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/simplebb.py @@ -0,0 +1,72 @@ +from .PyPolyBoRi import * +from .interred import interred + + +def buchberger(l): + "calculates a (non minimal) Groebner basis" + l = interred(l) + #for making sure, that every polynomial has a different leading term + #needed for add_generator + if not l: + return [] + g = GroebnerStrategy(l[0].ring()) + for p in l: + g.add_generator(p) + while g.npairs() > 0: + g.clean_top_by_chain_criterion() + p = g.next_spoly() + p = g.nf(p) + if not p.is_zero(): + g.add_generator(p) + return list(g) + + +def less_than_n_solutions(ideal, n): + l = interred(ideal) + if not l: + return False + g = GroebnerStrategy(l[0].ring()) + all_monomials = Monomial([Variable(i) for i + in range(number_of_variables())]).divisors() + monomials_not_in_leading_ideal = all_monomials + for p in l: + g.add_generator(p) + while g.npairs() > 0: + monomials_not_in_leading_ideal = monomials_not_in_leading_ideal \ + % g.reduction_strategy.minimal_leading_terms + if len(monomials_not_in_leading_ideal) < n: + return True + g.clean_top_by_chain_criterion() + p = g.next_spoly() + p = g.nf(p) + if not p.is_zero(): + g.add_generator(p) + monomials_not_in_leading_ideal = monomials_not_in_leading_ideal \ + % g.reduction_strategy.minimal_leading_terms + if len(monomials_not_in_leading_ideal) < n: + return True + else: + return False + + +def gauss(matrix): + """Toy Gaussian elimination. + Example: gauss([[0,1],[1,1]]) """ + from .gbcore import groebner_basis + + def get_num(idx, vars): + if idx in [var.index() for var in vars.variables()]: + return 1 + return 0 + + nrows = len(matrix) + ncols = len(matrix[0]) + eqs = [sum([matrix[row][col] * Variable(col) for col in range(ncols)]) + for row in range(nrows)] + result = groebner_basis(eqs) + result = result + [BooleConstant(0)] * (nrows - len(result)) + + return [[get_num(idx, elt.set().vars()) for idx in range(ncols)] + for elt in result] + + return result diff --git a/src/sage/rings/polynomial/pbori/specialsets.py b/src/sage/rings/polynomial/pbori/specialsets.py new file mode 100644 index 00000000000..10543969328 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/specialsets.py @@ -0,0 +1,103 @@ +from .PyPolyBoRi import (BooleSet, Polynomial, mod_mon_set, + if_then_else, Monomial, top_index, BooleConstant) + + +#def all_monomials_of_degree_d(d,variables): +# res=all_monomials_of_degree_d_new(d, variables) +# ref=all_monomials_of_degree_d_old(d, variables) +# assert res==ref, (d, variables) +# return res +def all_monomials_of_degree_d_old(d, variables): + + if d == 0: + return BooleConstant(1) + + if not variables: + return [] + variables = sorted(set(variables), reverse=True, key=top_index) + + m = variables[-1] + for v in variables[:-1]: + m = v + m + m = m.set() + i = 0 + res = Polynomial(variables[0].ring().one()).set() + while(i < d): + i = i + 1 + res = res.cartesian_product(m).diff(res) + return res + + +def all_monomials_of_degree_d(d, variables): + variables = Monomial(variables) + variables = list(variables.variables()) + if not variables: + assert d == 0 + return BooleConstant(1) + ring = variables[0].ring() + if d > len(variables): + return Polynomial(0, ring) + if d < 0: + return Polynomial(1, ring) + + deg_variables = variables[-d:] + #this ensures sorting by indices + res = Monomial(deg_variables) + + for i in range(1, len(variables) - d + 1): + deg_variables = variables[-d - i:-i] + res = Polynomial(res) + nav = res.navigation() + navs = [] + while not nav.constant(): + navs.append(BooleSet(nav, ring)) + nav = nav.then_branch() + acc = Polynomial(1, ring) + for (nav, v) in reversed(zip(navs, deg_variables)): + acc = if_then_else(v, acc, nav) + res = acc + return res.set() + + +def power_set(variables): + if not variables: + return BooleConstant(1) + variables = sorted(set(variables), reverse=True, key=top_index) + res = Polynomial(1, variables[0].ring()).set() + for v in variables: + res = if_then_else(v, res, res) + return res + +if __name__ == '__main__': + from .blocks import declare_ring, Block + r = declare_ring([Block("x", 10000)], globals()) + print(list(all_monomials_of_degree_d(0, [Variable(i) for i in range(100)]))) + print(list(all_monomials_of_degree_d(1, [Variable(i) for i in range(10)]))) + print(list(all_monomials_of_degree_d(2, [Variable(i) for i in range(4)]))) + print(list(all_monomials_of_degree_d(3, [Variable(i) for i in range(4)]))) + print(list(all_monomials_of_degree_d(4, [Variable(i) for i in range(4)]))) + print(list(all_monomials_of_degree_d(0, []))) + print(list(all_monomials_of_degree_d(1, []))) + print(list(power_set([Variable(i) for i in range(2)]))) + print(list(power_set([Variable(i) for i in range(4)]))) + print(list(power_set([]))) + #every monomial in the first 8 var, which is at most linear in the first 5 + print(list(mod_mon_set(power_set([Variable(i) for i in range(8)]), + all_monomials_of_degree_d(2, [Variable(i) for i in range(5)])))) + + #specialized normal form computation + print(Polynomial( + mod_mon_set( + (x(1) * x(2) + x(1) + 1).set(), + all_monomials_of_degree_d(2, [Variable(i) for i in range(1000)])))) + print(list(mod_mon_set(power_set([Variable(i) for i in range(50)]), + all_monomials_of_degree_d(2, [Variable(i) for i in range(1000)])))) + + +def monomial_from_indices(ring, indices): + l = sorted(indices, reverse=True) + res = Monomial(ring) + for i in l: + res = res * ring.variable(i) + + return res diff --git a/src/sage/rings/polynomial/pbori/statistics.py b/src/sage/rings/polynomial/pbori/statistics.py new file mode 100644 index 00000000000..36547b43d97 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/statistics.py @@ -0,0 +1,28 @@ +from .PyPolyBoRi import Monomial, Polynomial, top_index, BooleConstant + + +def used_vars(l, bound=None): + if not l: + return BooleConstant(1) + m = Monomial(Polynomial(next(iter(l))).vars_as_monomial()) + for p in l[1:]: + m = m * Polynomial(p).vars_as_monomial() + if bound and len(m) > bound: + return m + return m + + +def used_vars_set(l, bound=None): + if not l: + return BooleConstant(1) + s = set() + for p in l: + s.update(Polynomial(p).vars_as_monomial().variables()) + if bound and len(s) > bound: + break + sorted_s = sorted(list(s), key=top_index, reverse=True) + m = Monomial(next(iter(l)).ring()) + for v in sorted_s: + m = v * m + + return m From f9472934523e3cada46d4d18bd8e49e8e7d30a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 13 Aug 2020 12:08:59 +1200 Subject: [PATCH 184/379] replace "from sage.rings.polynomial.pbori" with "from sage.rings.polynomial.pbori.pbori" --- src/sage/crypto/boolean_function.pyx | 4 ++-- src/sage/crypto/mq/sr.py | 2 +- src/sage/libs/fes.pyx | 2 +- src/sage/misc/sageinspect.py | 2 +- .../polynomial/multi_polynomial_libsingular.pyx | 2 +- .../rings/polynomial/multi_polynomial_sequence.py | 12 ++++++------ src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 2 +- .../rings/polynomial/polynomial_ring_constructor.py | 2 +- src/sage/sat/converters/polybori.py | 2 +- src/sage/structure/sequence.py | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index 6ca784dfbff..820f4510708 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -37,7 +37,7 @@ from sage.structure.richcmp cimport rich_to_bool from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.polynomial.pbori import BooleanPolynomial +from sage.rings.polynomial.pbori.pbori import BooleanPolynomial from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro from sage.rings.polynomial.polynomial_element import is_Polynomial @@ -505,7 +505,7 @@ cdef class BooleanFunction(SageObject): bitset_init(anf, (1< """ - from sage.rings.polynomial.pbori import BooleanPolynomialRing + from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing R = self.ring() if not isinstance(R, BooleanPolynomialRing): from sage.libs.singular.groebner_strategy import GroebnerStrategy return GroebnerStrategy(self.ideal()) else: - from sage.rings.polynomial.pbori import GroebnerStrategy + from sage.rings.polynomial.pbori.pbori import GroebnerStrategy g = GroebnerStrategy(R) for p in self: g.add_as_you_wish(p) @@ -1444,7 +1444,7 @@ def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, ver [] """ - from sage.rings.polynomial.pbori import BooleanPolynomialRing + from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing from sage.modules.free_module import VectorSpace S = self @@ -1530,7 +1530,7 @@ def reduced(self): """ - from sage.rings.polynomial.pbori import BooleanPolynomialRing + from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing R = self.ring() if isinstance(R, BooleanPolynomialRing): diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index 0ad226d19a1..2fb9b1fc58e 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -64,7 +64,7 @@ """ from sage import all -from sage.rings.polynomial.pbori.pbori import * +from sage.rings.polynomial.pbori.pbori.pbori import * import weakref diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 98af2c449b8..6368ca18c12 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -948,7 +948,7 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): if not R is None: return R - from sage.rings.polynomial.pbori import BooleanPolynomialRing + from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing R = BooleanPolynomialRing(n, names, order) _save_in_cache(key, R) diff --git a/src/sage/sat/converters/polybori.py b/src/sage/sat/converters/polybori.py index 0eb892e937e..a53c4e650e3 100644 --- a/src/sage/sat/converters/polybori.py +++ b/src/sage/sat/converters/polybori.py @@ -28,7 +28,7 @@ ############################################################################## from random import Random -from sage.rings.polynomial.pbori import if_then_else as ite +from sage.rings.polynomial.pbori.pbori import if_then_else as ite from sage.rings.integer_ring import ZZ from sage.functions.other import ceil from sage.misc.cachefunc import cached_method, cached_function diff --git a/src/sage/structure/sequence.py b/src/sage/structure/sequence.py index 13b8e5d8ca3..238b65583b6 100644 --- a/src/sage/structure/sequence.py +++ b/src/sage/structure/sequence.py @@ -252,7 +252,7 @@ def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=Non universe = sage.structure.element.parent(x[len(x)-1]) from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - from sage.rings.polynomial.pbori import BooleanMonomialMonoid + from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.quotient_ring import is_QuotientRing From e7653a3417bf7657cfdb65a4685b74284f644b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 13 Aug 2020 12:10:13 +1200 Subject: [PATCH 185/379] Replace "from brial" with "from sage.rings.polynomial.pbori.brial" --- .../polynomial/multi_polynomial_sequence.py | 6 +- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 2 +- src/sage/rings/polynomial/pbori/addition.py | 10 +- src/sage/rings/polynomial/pbori/cnf.py | 14 +- src/sage/rings/polynomial/pbori/context.py | 4 +- .../polynomial/pbori/easy_polynomials.py | 4 +- src/sage/rings/polynomial/pbori/fglm.py | 8 +- src/sage/rings/polynomial/pbori/frontend.py | 2 +- src/sage/rings/polynomial/pbori/gbcore.py | 2 +- src/sage/rings/polynomial/pbori/intersect.py | 4 +- src/sage/rings/polynomial/pbori/intpolys.py | 2 +- src/sage/rings/polynomial/pbori/ll.py | 8 +- src/sage/rings/polynomial/pbori/nf.py | 2 +- src/sage/rings/polynomial/pbori/parallel.py | 6 +- src/sage/rings/polynomial/pbori/pbori.pyx | 224 +++++++++--------- src/sage/rings/polynomial/pbori/randompoly.py | 2 +- src/sage/sat/converters/__init__.py | 2 +- 17 files changed, 151 insertions(+), 151 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index a3c3903e580..91d096dc9d9 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1249,8 +1249,8 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc This is called "massaging" in [BCJ2007]_. """ from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing - from brial import gauss_on_polys - from brial.ll import eliminate,ll_encode,ll_red_nf_redsb + from sage.rings.polynomial.pbori.brial import gauss_on_polys + from sage.rings.polynomial.pbori.brial.ll import eliminate,ll_encode,ll_red_nf_redsb R = self.ring() @@ -1534,7 +1534,7 @@ def reduced(self): R = self.ring() if isinstance(R, BooleanPolynomialRing): - from brial.interred import interred as inter_red + from sage.rings.polynomial.pbori.brial.interred import interred as inter_red l = [p for p in self if not p==0] l = sorted(inter_red(l, completely=True), reverse=True) return PolynomialSequence(l, R, immutable=True) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index 2fb9b1fc58e..7dccea1c8e2 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -8,7 +8,7 @@ Examples: - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) >>> x0>x1 True diff --git a/src/sage/rings/polynomial/pbori/addition.py b/src/sage/rings/polynomial/pbori/addition.py index 2edae791592..5b142415888 100644 --- a/src/sage/rings/polynomial/pbori/addition.py +++ b/src/sage/rings/polynomial/pbori/addition.py @@ -6,7 +6,7 @@ def add_bits_old(bits): """Adds n bits - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r=Ring(10) >>> add_bits_old([r.variable(i) for i in xrange(3)]) [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] @@ -33,7 +33,7 @@ def add_bits_old(bits): def add_bits(bits): """Adds n bit variables, by Lucas theorem - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r=Ring(10) >>> add_bits([r.variable(i) for i in xrange(3)]) [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] @@ -59,7 +59,7 @@ def add_bits(bits): def add_bit_expressions(bit_expressions): """Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r=Ring(20) >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] @@ -85,7 +85,7 @@ def add_bit_expressions(bit_expressions): def add_words(words): """def adds n words, this words are supposed to consists of list of their bits. - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r=Ring(1000) >>> add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] @@ -112,7 +112,7 @@ def add_words(words): def multiply_by_addition(word_a, word_b): """Multiply two words - >>> from brial import Ring + >>> from sage.rings.polynomial.pbori.brial import Ring >>> r=Ring(1000) >>> x = r.variable >>> n=7 diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py index 4064c598054..f57d38894a9 100644 --- a/src/sage/rings/polynomial/pbori/cnf.py +++ b/src/sage/rings/polynomial/pbori/cnf.py @@ -14,7 +14,7 @@ def __init__(self, r, random_seed=16): def zero_blocks(self, f): """divides the zero set of f into blocks - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r = declare_ring(["x", "y", "z"], dict()) >>> e = CNFEncoder(r) >>> e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) @@ -78,7 +78,7 @@ def get_val(var): def clauses(self, f): """ - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r = declare_ring(["x", "y", "z"], dict()) >>> e = CNFEncoder(r) >>> e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS @@ -102,7 +102,7 @@ def clauses(self, f): def polynomial_clauses(self, f): """ - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r = declare_ring(["x", "y", "z"], dict()) >>> e = CNFEncoder(r) >>> e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) @@ -141,7 +141,7 @@ def get_sign(val): def dimacs_encode_polynomial(self, p): """ - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> d=dict() >>> r = declare_ring(["x", "y", "z"], d) >>> e = CNFEncoder(r) @@ -156,7 +156,7 @@ def dimacs_encode_polynomial(self, p): def dimacs_cnf(self, polynomial_system): r""" - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r = declare_ring(["x", "y", "z"], dict()) >>> e = CNFEncoder(r) >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) @@ -182,7 +182,7 @@ class CryptoMiniSatEncoder(CNFEncoder): def dimacs_encode_polynomial(self, p): r""" - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> d=dict() >>> r = declare_ring(["x", "y", "z"], d) >>> e = CryptoMiniSatEncoder(r) @@ -216,7 +216,7 @@ def dimacs_encode_polynomial(self, p): def dimacs_cnf(self, polynomial_system): r""" - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r = declare_ring(["x", "y", "z"], dict()) >>> e = CryptoMiniSatEncoder(r) >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) diff --git a/src/sage/rings/polynomial/pbori/context.py b/src/sage/rings/polynomial/pbori/context.py index bdd1d8e05d0..b1d9301d1c9 100644 --- a/src/sage/rings/polynomial/pbori/context.py +++ b/src/sage/rings/polynomial/pbori/context.py @@ -25,7 +25,7 @@ class FactoryContext(object): Example: >>> r = Ring(1000) - >>> from brial import Variable + >>> from sage.rings.polynomial.pbori.brial import Variable >>> def var(idx): return Variable(idx, r) >>> with FactoryContext(Variable, var): ... print Variable(17) @@ -65,7 +65,7 @@ class RingContext(object): Example: >>> r = Ring(1000) - >>> from brial import Variable + >>> from sage.rings.polynomial.pbori.brial import Variable >>> print Variable(17, r) x(17) >>> with RingContext(r): diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py index 11e4793495c..28c72f18324 100644 --- a/src/sage/rings/polynomial/pbori/easy_polynomials.py +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -5,7 +5,7 @@ def easy_linear_polynomials(p): """ Get linear polynomials implied by given polynomial. - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> easy_linear_polynomials(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] >>> easy_linear_polynomials(x(1)*x(2) + 0) @@ -28,7 +28,7 @@ def easy_linear_polynomials_via_interpolation(p): """ Get linear polynomials implied by given polynomial using interpolation of the variety. - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] >>> easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index fa4e1d7d080..5ab560294be 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -25,7 +25,7 @@ def fglm(I, from_ring, to_ring): converts *reduced* Groebner Basis in from_ring to a GroebnerBasis in to_ring. It acts independend of the global ring, which is restored at the end of the computation, - >>> from brial.PyPolyBoRi import OrderCode + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode >>> dp_asc = OrderCode.dp_asc >>> r=declare_ring(['x','y','z'],dict()) >>> old_ring = r @@ -44,9 +44,9 @@ def fglm(I, from_ring, to_ring): def vars_real_divisors(monomial, monomial_set): """ returns all elements of of monomial_set, which result multiplied by a variable in monomial. - >>> from brial.PyPolyBoRi import OrderCode + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode >>> dp_asc = OrderCode.dp_asc - >>> from brial.PyPolyBoRi import Ring + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring >>> r=Ring(1000) >>> x = r.variable >>> b=BooleSet([x(1)*x(2),x(2)]) @@ -60,7 +60,7 @@ def vars_real_divisors(monomial, monomial_set): def m_k_plus_one(completed_elements, variables): """ calculates $m_{k+1}$ from the FGLM algorithm as described in Wichmanns diploma thesis It would be nice to be able to efficiently extract the smallest term of a polynomial - >>> from brial.PyPolyBoRi import OrderCode + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode >>> dp_asc = OrderCode.dp_asc >>> r=Ring(1000) >>> x = r.variable diff --git a/src/sage/rings/polynomial/pbori/frontend.py b/src/sage/rings/polynomial/pbori/frontend.py index 4cfb8816a77..b6cde7663ab 100644 --- a/src/sage/rings/polynomial/pbori/frontend.py +++ b/src/sage/rings/polynomial/pbori/frontend.py @@ -17,7 +17,7 @@ >>> x(9999) + x(9999) 0 ->>> from brial.frontend import * +>>> from sage.rings.polynomial.pbori.brial.frontend import * >>> context = dict(globals()) >>> polybori_start(context) # doctest: +ELLIPSIS ipbori... diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index fd3c3d0c2e1..bc65df909aa 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -387,7 +387,7 @@ def variety_size_from_gb(I): def other_ordering_pre(I, option_set, kwds): """ - >>> from brial.blocks import declare_ring + >>> from sage.rings.polynomial.pbori.brial.blocks import declare_ring >>> r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) >>> id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] >>> groebner_basis(id) diff --git a/src/sage/rings/polynomial/pbori/intersect.py b/src/sage/rings/polynomial/pbori/intersect.py index be91eeccf62..0548d18ea70 100644 --- a/src/sage/rings/polynomial/pbori/intersect.py +++ b/src/sage/rings/polynomial/pbori/intersect.py @@ -16,8 +16,8 @@ def intersect(i, j, **gb_opts): This functions intersects two ideals. The first ring variable is used as helper variable for this intersection. It is assumed, that it doesn't occur in the ideals, and that we have an elimination ordering for this variables. Both assumptions are checked. - >>> from brial.frontend import declare_ring - >>> from brial import Block + >>> from sage.rings.polynomial.pbori.brial.frontend import declare_ring + >>> from sage.rings.polynomial.pbori.brial import Block >>> r=declare_ring(Block("x", 1000), globals()) >>> x = r.variable >>> intersect([x(1),x(2)+1],[x(1),x(2)]) diff --git a/src/sage/rings/polynomial/pbori/intpolys.py b/src/sage/rings/polynomial/pbori/intpolys.py index ac7a1445c00..c90f3037743 100644 --- a/src/sage/rings/polynomial/pbori/intpolys.py +++ b/src/sage/rings/polynomial/pbori/intpolys.py @@ -33,7 +33,7 @@ def __coerce__(self, other): def __add__(self, other): """ - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS >>> p=IntegerPolynomial(x(1)) >>> p diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index 16defde38ee..bab9536ac3d 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -209,7 +209,7 @@ def llnf(p): class RingMap(object): """Define a mapping between two rings by common variable names. - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> to_ring = declare_ring([Block("x", 10)], globals()) >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) >>> mapping = RingMap(to_ring, from_ring) @@ -232,7 +232,7 @@ class RingMap(object): def __init__(self, to_ring, from_ring): """Initialize map by two given rings. - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> to_ring = declare_ring([Block("x", 10)], globals()) >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) >>> mapping = RingMap(to_ring, from_ring) @@ -268,7 +268,7 @@ def indices(vars): def __call__(self, poly): """Execute the map to change rings. - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> to_ring = declare_ring([Block("x", 10)], globals()) >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) >>> mapping = RingMap(to_ring, from_ring) @@ -280,7 +280,7 @@ def __call__(self, poly): def invert(self, poly): """Inverted map to initial ring. - >>> from brial.frontend import * + >>> from sage.rings.polynomial.pbori.brial.frontend import * >>> to_ring = declare_ring([Block("x", 10)], globals()) >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) >>> mapping = RingMap(to_ring, from_ring) diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index fa0ab072807..ef99c9afda0 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -643,7 +643,7 @@ def symmGB_F2_C(G, opt_exchange=True, def normal_form(poly, ideal, reduced=True): """ Simple normal form computation of a polynomial against an ideal. - >>> from brial import declare_ring, normal_form + >>> from sage.rings.polynomial.pbori.brial import declare_ring, normal_form >>> r=declare_ring(['x','y'], globals()) >>> normal_form(x+y, [y],reduced=True) x diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 8af6176653c..4accb47b6a5 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -40,7 +40,7 @@ def to_fast_pickable(l): Each code c refers to the c-2-th position in the conversion list, if c >=2, else to the corresponding Boolean constant if c in {0, 1} EXAMPLES: - >>> from brial.PyPolyBoRi import Ring + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring >>> r=Ring(1000) >>> x=r.variable >>> to_fast_pickable([Polynomial(1, r)]) @@ -109,7 +109,7 @@ def from_fast_pickable(l, r): OUTPUT: a list of Boolean polynomials EXAMPLES: - >>> from brial.PyPolyBoRi import Ring + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring >>> r=Ring(1000) >>> x = r.variable >>> from_fast_pickable([[1], []], r) @@ -257,7 +257,7 @@ def groebner_basis_first_finished(I, *l): - tries to compute groebner_basis(I, **kwd) for kwd in l - returns the result of the first terminated computation EXAMPLES: - >>> from brial.PyPolyBoRi import Ring + >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring >>> r=Ring(1000) >>> ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] >>> #groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 81567ba2e2f..78ec68ef798 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -157,7 +157,7 @@ Access to the original PolyBoRi interface The re-implementation PolyBoRi's native wrapper is available to the user too:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: declare_ring([Block('x',2),Block('y',3)],globals()) Boolean PolynomialRing in x0, x1, y0, y1, y2 sage: r @@ -1511,7 +1511,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): ALGORITHM: Calls ``interpolate_smallest_lex`` as described in the PolyBoRi tutorial. """ - #from brial.interpolate import interpolate_smallest_lex + #from sage.rings.polynomial.pbori.brial.interpolate import interpolate_smallest_lex from sage.misc.misc_c import prod n = self.ngens() x = self.gens() @@ -1837,7 +1837,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: M @@ -1862,7 +1862,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): TESTS:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: B. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(B) sage: M @@ -1886,7 +1886,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): """ EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: M # indirect doctest @@ -1900,7 +1900,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: {M:1} # indirect doctest @@ -1914,7 +1914,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P = BooleanPolynomialRing(100, 'x') sage: M = BooleanMonomialMonoid(P) sage: M.ngens() @@ -1932,7 +1932,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.gen(0) @@ -1959,7 +1959,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.gens() @@ -1973,7 +1973,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.get_action(ZZ) # indirect doctest @@ -1993,7 +1993,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x_monom = M(x); x_monom @@ -2004,7 +2004,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): Convert elements from :class:`BooleanMonomialMonoid` where the generators of self include the generators of the other monoid:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: N = BooleanMonomialMonoid(R) sage: m = M(N(y*z)); m @@ -2014,7 +2014,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): TESTS:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: N = BooleanMonomialMonoid(R) sage: M(N(y)) @@ -2024,7 +2024,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): ... ValueError: cannot convert monomial t to MonomialMonoid of Boolean PolynomialRing in x, y, z: name t not defined - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(4) sage: N = BooleanMonomialMonoid(R) sage: M(N(x*y*z)) @@ -2061,7 +2061,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x_monom = M(x); x_monom @@ -2200,7 +2200,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid, BooleanMonomial + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid, BooleanMonomial sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: BooleanMonomial(M) @@ -2215,7 +2215,7 @@ cdef class BooleanMonomial(MonoidElement): """ EXAMPLES:: - sage: from brial import BooleanMonomialMonoid, BooleanMonomial + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid, BooleanMonomial sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: BooleanMonomial(M) @@ -2235,7 +2235,7 @@ cdef class BooleanMonomial(MonoidElement): TESTS:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(R) sage: t = M.0*M.1 @@ -2251,7 +2251,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x) < M(y) @@ -2427,7 +2427,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*y).deg() @@ -2455,7 +2455,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*y).degree() @@ -2560,7 +2560,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: len(M(x*y)) @@ -2574,7 +2574,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: list(M(x*z)) # indirect doctest @@ -2588,7 +2588,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*z).variables() # indirect doctest @@ -2602,7 +2602,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: list(M(x*z).iterindex()) @@ -2616,7 +2616,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y); z=M(z) @@ -2642,7 +2642,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y) @@ -2686,7 +2686,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y) @@ -2738,7 +2738,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from brial import BooleSet + sage: from sage.rings.polynomial.pbori.brial import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -2908,7 +2908,7 @@ cdef class BooleanPolynomial(MPolynomial): TESTS:: - sage: from brial import BooleanPolynomial + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomial sage: B. = BooleanPolynomialRing(3) sage: BooleanPolynomial(B) 0 @@ -4118,7 +4118,7 @@ cdef class BooleanPolynomial(MPolynomial): sage: loads(dumps(a)) == a True """ - from brial.parallel import _encode_polynomial + from sage.rings.polynomial.pbori.brial.parallel import _encode_polynomial return unpickle_BooleanPolynomial0, (self._parent, _encode_polynomial(self)) @@ -4345,7 +4345,7 @@ cdef class BooleanPolynomial(MPolynomial): EXAMPLES:: - sage: from brial import BooleSet + sage: from sage.rings.polynomial.pbori.brial import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -4706,7 +4706,7 @@ cdef class BooleanPolynomial(MPolynomial): ... TypeError: argument must be a BooleanPolynomial. """ - from brial import red_tail + from sage.rings.polynomial.pbori.brial import red_tail if not I: return self if isinstance(I, BooleanPolynomialIdeal): @@ -4732,7 +4732,7 @@ cdef class PolynomialConstruct: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: PolynomialConstruct().lead(a) a @@ -4746,7 +4746,7 @@ cdef class PolynomialConstruct: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: PolynomialConstruct()(1, B) 1 @@ -4783,7 +4783,7 @@ cdef class MonomialConstruct: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: MonomialConstruct()(B) 1 @@ -4820,7 +4820,7 @@ cdef class VariableConstruct: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: VariableConstruct()(B) a @@ -5093,7 +5093,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): [x*z, x*y + y*z + y] """ - from brial.gbcore import groebner_basis + from sage.rings.polynomial.pbori.brial.gbcore import groebner_basis if self.ring().term_order()[0].name() == "degrevlex": # PolyBoRi's groebner_basis assumes increasing indices @@ -5190,7 +5190,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): sage: I.reduce(gb[0]*B.gen(1)) 0 """ - from brial import red_tail + from sage.rings.polynomial.pbori.brial import red_tail try: g = self.__gb except AttributeError: @@ -5318,7 +5318,7 @@ cdef class BooleSet: EXAMPLES:: - sage: from brial import BooleSet + sage: from sage.rings.polynomial.pbori.brial import BooleSet sage: B. = BooleanPolynomialRing(4) sage: BS = BooleSet(a.set()) sage: BS @@ -5328,7 +5328,7 @@ cdef class BooleSet: sage: BS {{a,b}, {c}, {}} - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: BooleSet([Monomial(B)]) {{}} @@ -5375,7 +5375,7 @@ cdef class BooleSet: """ EXAMPLES:: - sage: from brial import BooleSet + sage: from sage.rings.polynomial.pbori.brial import BooleSet sage: B. = BooleanPolynomialRing(4) sage: BS = BooleSet(B) sage: repr(BS) # indirect doctest @@ -5427,7 +5427,7 @@ cdef class BooleSet: EXAMPLES:: - sage: from brial import BooleSet + sage: from sage.rings.polynomial.pbori.brial import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -6033,7 +6033,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6054,7 +6054,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6077,7 +6077,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: list(iter(v)) @@ -6090,7 +6090,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector() sage: len(v) @@ -6106,7 +6106,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6136,7 +6136,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6168,7 +6168,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: v = BooleanPolynomialVector() sage: for i in range(5): ....: v.append(B.random_element()) @@ -6231,7 +6231,7 @@ cdef class ReductionStrategy: """ EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: del red @@ -6249,7 +6249,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x) @@ -6279,7 +6279,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -6303,7 +6303,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -6328,7 +6328,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6350,7 +6350,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(a*b + c + 1) @@ -6374,7 +6374,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(a*b + c + 1) @@ -6414,7 +6414,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6476,7 +6476,7 @@ cdef class ReductionStrategy: """ EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6513,7 +6513,7 @@ cdef class FGLMStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: x > y > z True @@ -6561,7 +6561,7 @@ cdef class FGLMStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: ideal = BooleanPolynomialVector([x+z, y+z]) sage: list(ideal) @@ -6593,7 +6593,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: B. = BooleanPolynomialRing() sage: G = GroebnerStrategy(B) sage: H = GroebnerStrategy(G) @@ -6624,7 +6624,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_generator(a + b) @@ -6651,7 +6651,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_generator(a + b) @@ -6679,7 +6679,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_as_you_wish(a + b) @@ -6738,7 +6738,7 @@ cdef class GroebnerStrategy: spanned by the generators but not in the set of generators:: sage: B. = BooleanPolynomialRing() - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6752,7 +6752,7 @@ cdef class GroebnerStrategy: Still, we have that:: - sage: from brial import groebner_basis + sage: from sage.rings.polynomial.pbori.brial import groebner_basis sage: groebner_basis(gb) [1] """ @@ -6769,7 +6769,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6779,7 +6779,7 @@ cdef class GroebnerStrategy: sage: gb.add_generator(a*b + b + c*e + e + 1) sage: gb.add_generator(a + b + c*d + c*e + 1) - sage: from brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector sage: V= BooleanPolynomialVector([b*d, a*b]) sage: list(gb.faugere_step_dense(V)) [b + c*e + e + 1, c + d*f + e + f] @@ -6845,7 +6845,7 @@ cdef class GroebnerStrategy: """ EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_as_you_wish(a + b) @@ -6878,7 +6878,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6889,7 +6889,7 @@ cdef class GroebnerStrategy: sage: gb.variable_has_value(0) False - sage: from brial import groebner_basis + sage: from sage.rings.polynomial.pbori.brial import groebner_basis sage: g = groebner_basis(gb) sage: list(g) [a, b + 1, c + 1, d, e + 1, f] @@ -6918,7 +6918,7 @@ cdef class GroebnerStrategy: sage: I = B.ideal([B(f) for f in I.gens()]) sage: gb = I.groebner_basis() - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: G = GroebnerStrategy(B) sage: _ = [G.add_generator(f) for f in gb] @@ -6953,7 +6953,7 @@ cdef class GroebnerStrategy: sage: B. = BooleanPolynomialRing() sage: f = B.random_element() sage: g = B.random_element() - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: strat = GroebnerStrategy(B) sage: strat.add_generator(f) sage: strat.add_generator(g) @@ -6973,7 +6973,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy sage: G = GroebnerStrategy(B) sage: G.add_as_you_wish(a) @@ -7057,7 +7057,7 @@ cdef class BooleanMulAction(Action): """ EXAMPLES:: - sage: from brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y); z=M(z) @@ -7114,7 +7114,7 @@ def add_up_polynomials(BooleanPolynomialVector v, BooleanPolynomial init): EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: v = BooleanPolynomialVector() sage: l = [B.random_element() for _ in range(5)] @@ -7143,7 +7143,7 @@ def red_tail(ReductionStrategy s, BooleanPolynomial p): EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -7165,7 +7165,7 @@ def map_every_x_to_x_plus_one(BooleanPolynomial p): sage: B. = BooleanPolynomialRing(3) sage: f = a*b + z + 1; f a*b + z + 1 - sage: from brial import map_every_x_to_x_plus_one + sage: from sage.rings.polynomial.pbori.brial import map_every_x_to_x_plus_one sage: map_every_x_to_x_plus_one(f) a*b + a + b + z + 1 sage: f(a+1,b+1,z+1) @@ -7201,7 +7201,7 @@ def zeros(pol, BooleSet s): This encodes the points (1,1,1,0), (1,1,0,0), (0,0,1,1) and (0,1,1,0). But of these only (1,1,0,0) evaluates to zero.:: - sage: from brial import zeros + sage: from sage.rings.polynomial.pbori.brial import zeros sage: zeros(f,s) {{a,b}} @@ -7236,7 +7236,7 @@ def interpolate(zero, one): sage: B = BooleanPolynomialRing(4,"x0,x1,x2,x3") sage: x = B.gen - sage: from brial.interpolate import * + sage: from sage.rings.polynomial.pbori.brial.interpolate import * sage: V=(x(0)+x(1)+x(2)+x(3)+1).set() sage: V @@ -7301,7 +7301,7 @@ def interpolate_smallest_lex(zero, one): sage: B = BooleanPolynomialRing(4,"x0,x1,x2,x3") sage: x = B.gen - sage: from brial.interpolate import * + sage: from sage.rings.polynomial.pbori.brial.interpolate import * sage: V=(x(0)+x(1)+x(2)+x(3)+1).set() We take V = {e0,e1,e2,e3,0}, where ei describes the i-th unit @@ -7389,7 +7389,7 @@ def ll_red_nf_redsb(p, BooleSet reductors): EXAMPLES:: - sage: from brial import ll_red_nf_redsb + sage: from sage.rings.polynomial.pbori.brial import ll_red_nf_redsb sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7431,7 +7431,7 @@ def ll_red_nf_noredsb(BooleanPolynomial p, BooleSet reductors): EXAMPLES:: - sage: from brial import ll_red_nf_noredsb + sage: from sage.rings.polynomial.pbori.brial import ll_red_nf_noredsb sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7464,7 +7464,7 @@ def ll_red_nf_noredsb_single_recursive_call(BooleanPolynomial p, BooleSet reduct EXAMPLES:: - sage: from brial import ll_red_nf_noredsb_single_recursive_call + sage: from sage.rings.polynomial.pbori.brial import ll_red_nf_noredsb_single_recursive_call sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7505,7 +7505,7 @@ def if_then_else(root, a, b): EXAMPLES:: - sage: from brial import if_then_else + sage: from sage.rings.polynomial.pbori.brial import if_then_else sage: B = BooleanPolynomialRing(6,'x') sage: x0,x1,x2,x3,x4,x5 = B.gens() sage: f0 = x2*x3+x3 @@ -7584,7 +7584,7 @@ def top_index(s): EXAMPLES:: sage: B. = BooleanPolynomialRing(3) - sage: from brial import top_index + sage: from sage.rings.polynomial.pbori.brial import top_index sage: top_index(x.lm()) 0 sage: top_index(y*z) @@ -7691,7 +7691,7 @@ def gauss_on_polys(inp): EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: l = [B.random_element() for _ in range(B.ngens())] sage: A,v = Sequence(l,B).coefficient_matrix() sage: A @@ -7734,7 +7734,7 @@ def substitute_variables(BooleanPolynomialRing parent, vec, BooleanPolynomial po sage: B. = BooleanPolynomialRing() sage: f = a*b + c + 1 - sage: from brial import substitute_variables + sage: from sage.rings.polynomial.pbori.brial import substitute_variables sage: substitute_variables(B, [a,b,c],f) a*b + c + 1 sage: substitute_variables(B, [a+1,b,c],f) @@ -7750,7 +7750,7 @@ def substitute_variables(BooleanPolynomialRing parent, vec, BooleanPolynomial po sage: f = a*b + c + 1 sage: B. = BooleanPolynomialRing(order='deglex') - sage: from brial import substitute_variables + sage: from sage.rings.polynomial.pbori.brial import substitute_variables sage: substitute_variables(B, [x,y,z], f) * w w*x*y + w*z + w @@ -7772,7 +7772,7 @@ def set_random_seed(seed): EXAMPLES:: - sage: from brial import random_set, set_random_seed + sage: from sage.rings.polynomial.pbori.brial import random_set, set_random_seed sage: B. = BooleanPolynomialRing() sage: (a*b*c*d).lm() a*b*c*d @@ -7798,7 +7798,7 @@ def random_set(BooleanMonomial variables, length): EXAMPLES:: - sage: from brial import random_set, set_random_seed + sage: from sage.rings.polynomial.pbori.brial import random_set, set_random_seed sage: B. = BooleanPolynomialRing() sage: (a*b*c*d).lm() a*b*c*d @@ -7815,7 +7815,7 @@ def easy_linear_factors(BooleanPolynomial p): return new_BPV_from_PBPolyVector(p._parent, pb_easy_linear_factors(p._pbpoly)) -# todo: merge with pickling from brial.parallel +# todo: merge with pickling from sage.rings.polynomial.pbori.brial.parallel def unpickle_BooleanPolynomial(ring, string): """ Unpickle boolean polynomials @@ -7830,7 +7830,7 @@ def unpickle_BooleanPolynomial(ring, string): return ring(eval(string,ring.gens_dict())) -# todo: merge with pickling from brial.parallel +# todo: merge with pickling from sage.rings.polynomial.pbori.brial.parallel def unpickle_BooleanPolynomial0(ring, l): """ Unpickle boolean polynomials @@ -7842,11 +7842,11 @@ def unpickle_BooleanPolynomial0(ring, l): sage: loads(dumps(a+b)) == a+b # indirect doctest True """ - from brial.parallel import _decode_polynomial + from sage.rings.polynomial.pbori.brial.parallel import _decode_polynomial return _decode_polynomial(l) -# todo: merge with pickling from brial.parallel +# todo: merge with pickling from sage.rings.polynomial.pbori.brial.parallel def unpickle_BooleanPolynomialRing(n, names, order): """ Unpickle boolean polynomial rings. @@ -7873,7 +7873,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: [BooleConstant(i) for i in range(5)] [0, 1, 0, 1, 0] """ @@ -7883,7 +7883,7 @@ cdef class BooleConstant: """ EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: repr((BooleConstant(0),BooleConstant(1))) # indirect doctest '(0, 1)' @@ -7898,7 +7898,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: BooleConstant(0).deg() -1 sage: BooleConstant(1).deg() @@ -7912,7 +7912,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: BooleConstant(0).variables() () sage: BooleConstant(1).variables() @@ -7926,7 +7926,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: BooleConstant(0).is_one() False sage: BooleConstant(1).is_one() @@ -7940,7 +7940,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: BooleConstant(1).is_zero() False sage: BooleConstant(0).is_zero() @@ -7954,7 +7954,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: BooleConstant(1).is_constant() True sage: BooleConstant(0).is_constant() @@ -7968,7 +7968,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from brial import BooleConstant + sage: from sage.rings.polynomial.pbori.brial import BooleConstant sage: BooleConstant(1).has_constant_part() True sage: BooleConstant(0).has_constant_part() @@ -8017,7 +8017,7 @@ cdef class VariableFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: fac = VariableFactory() sage: fac = VariableFactory(B) @@ -8033,7 +8033,7 @@ cdef class VariableFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: VariableFactory()(B) a @@ -8066,7 +8066,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: fac = MonomialFactory() sage: fac = MonomialFactory(B) @@ -8079,7 +8079,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: MonomialFactory()(B) 1 @@ -8100,7 +8100,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: MonomialFactory()(B) 1 @@ -8145,7 +8145,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: fac = PolynomialFactory() @@ -8161,7 +8161,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: PolynomialFactory().lead(a) a @@ -8175,7 +8175,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from brial import * + sage: from sage.rings.polynomial.pbori.brial import * sage: B. = BooleanPolynomialRing() sage: PolynomialFactory()(1, B) 1 diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index 5bf74060cac..0d7655222bf 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -35,7 +35,7 @@ def sparse_random_system(ring, number_of_polynomials, generates a system, which is sparse in the sense, that each polynomial contains only a small subset of variables. In each variable that occurrs in a polynomial it is dense in the terms up to the given degree (every term occurs with probability 1/2). The system will be satisfiable by at least one solution. - >>> from brial import * + >>> from sage.rings.polynomial.pbori.brial import * >>> r=Ring(10) >>> s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) >>> [p.deg() for p in s] diff --git a/src/sage/sat/converters/__init__.py b/src/sage/sat/converters/__init__.py index d6e55fdc292..01ac16511ae 100644 --- a/src/sage/sat/converters/__init__.py +++ b/src/sage/sat/converters/__init__.py @@ -1,3 +1,3 @@ from __future__ import absolute_import from .anf2cnf import ANF2CNFConverter -from brial.cnf import CNFEncoder as PolyBoRiCNFEncoder +from sage.rings.polynomial.pbori.brial.cnf import CNFEncoder as PolyBoRiCNFEncoder From 2226097a9bf79f4b9c9675fc9fe73ffd043819e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 12 Aug 2020 19:06:09 -0700 Subject: [PATCH 186/379] src/sage/rings/polynomial/pbori/__init__.py: Draft of lazy_import call --- src/sage/rings/polynomial/pbori/__init__.py | 72 +++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/sage/rings/polynomial/pbori/__init__.py b/src/sage/rings/polynomial/pbori/__init__.py index 6a792e2cd27..c50969fdf72 100644 --- a/src/sage/rings/polynomial/pbori/__init__.py +++ b/src/sage/rings/polynomial/pbori/__init__.py @@ -43,3 +43,75 @@ def plist(a, b): return [a, b] + +# Advertised reimports +# ... any from below? ... + +# Deprecated reimports +lazy_import('sage.rings.polynomial.pbori.pbori', + ['BooleConstant', + 'BooleSet', + 'BooleSetIterator', + 'BooleanMonomial', + 'BooleanMonomialIterator', + 'BooleanMonomialMonoid', + 'BooleanMonomialVariableIterator', + 'BooleanMulAction', + 'BooleanPolynomial', + 'BooleanPolynomialEntry', + 'BooleanPolynomialIdeal', + 'BooleanPolynomialIterator', + 'BooleanPolynomialRing', + 'BooleanPolynomialVector', + 'BooleanPolynomialVectorIterator', + 'CCuddNavigator', + 'FGLMStrategy', + 'GroebnerStrategy', + 'MonomialConstruct', + 'MonomialFactory', + 'PolynomialConstruct', + 'PolynomialFactory', + 'ReductionStrategy', + 'TermOrder_from_pb_order', + 'VariableBlock', + 'VariableConstruct', + 'VariableFactory', + 'add_up_polynomials', + 'block_dlex', + 'block_dp_asc', + 'contained_vars', + 'dlex', + 'dp', + 'dp_asc', + 'easy_linear_factors', + 'gauss_on_polys', + 'get_var_mapping', + 'if_then_else', + 'interpolate', + 'interpolate_smallest_lex', + 'inv_order_dict', + 'll_red_nf_noredsb', + 'll_red_nf_noredsb_single_recursive_call', + 'll_red_nf_redsb', + 'lp', + 'map_every_x_to_x_plus_one', + 'mod_mon_set', + 'mod_var_set', + 'mult_fact_sim_C', + 'nf3', + 'order_dict', + 'order_mapping', + 'parallel_reduce', + 'random_set', + 'recursively_insert', + 'red_tail', + 'rings', + 'set_random_seed', + 'singular_default', + 'substitute_variables', + 'top_index', + 'unpickle_BooleanPolynomial', + 'unpickle_BooleanPolynomial0', + 'unpickle_BooleanPolynomialRing', + 'zeros'], + deprecation=30332) From a6e00aa3c26a14fbad2b953197e9b9077436b7ee Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 00:39:53 -0600 Subject: [PATCH 187/379] Standardize TeX in docstrings --- src/sage/combinat/fully_commutative_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 40baf3f7bb9..fe4c5a010bb 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -275,7 +275,7 @@ def plot_heap(self): r""" Display the Hasse diagram of the heap of ``self``. - The Hasse diagram is rendered in the lattice `S \times \mathbb{N}`, with + The Hasse diagram is rendered in the lattice `S \times \NN`, with every element `i` in the poset drawn as a point labelled by its label `s_i`. Every point is placed in the column for its label at a certain level. The levels start at 0 and the level k of an element `i` is the From a1b56af29eadeaabb5d19369ad4e02cdbb34319d Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 00:42:44 -0600 Subject: [PATCH 188/379] Make still_reduced_fc_after_prepending private. --- .../combinat/fully_commutative_elements.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index fe4c5a010bb..971baee10f8 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -558,7 +558,7 @@ def coset_decomposition(self, J, side='left'): # The following function uses coset decompositions and will help us # generate all FC elements in a Coxeter group by induction on length. - def still_reduced_fc_after_prepending(self, s): + def _still_reduced_fc_after_prepending(self, s): r""" Determine if ``self`` prepended with ``s`` is still a reduced word of an FC element in the Coxeter system. @@ -577,33 +577,33 @@ def still_reduced_fc_after_prepending(self, s): When `s=1`, `sw` is 112, which is not reduced:: - sage: w.still_reduced_fc_after_prepending(1) + sage: w._still_reduced_fc_after_prepending(1) False When `s=2`, `sw` is 212, which is reduced but not FC:: - sage: w.still_reduced_fc_after_prepending(2) + sage: w._still_reduced_fc_after_prepending(2) False When `s=31, `sw` is 312, which is reduced and FC:: - sage: w.still_reduced_fc_after_prepending(3) + sage: w._still_reduced_fc_after_prepending(3) True More examples:: sage: u = FCB3([3,1,2]) - sage: u.still_reduced_fc_after_prepending(1) + sage: u._still_reduced_fc_after_prepending(1) False - sage: u.still_reduced_fc_after_prepending(2) + sage: u._still_reduced_fc_after_prepending(2) True - sage: u.still_reduced_fc_after_prepending(3) + sage: u._still_reduced_fc_after_prepending(3) False sage: FCA5 = CoxeterGroup(['A', 5]).fully_commutative_elements() sage: w = FCA5([2,4,1,3,2,5]) - sage: w.still_reduced_fc_after_prepending(5) + sage: w._still_reduced_fc_after_prepending(5) False .. NOTE:: @@ -1063,7 +1063,7 @@ def __iter__(self): new_words = {} for w in recent_words: for s in letters: - if w.still_reduced_fc_after_prepending(s): + if w._still_reduced_fc_after_prepending(s): sw = self.element_class( self, [s] + list(w), check=False) # "Add" sw to the "set" From d4b00b6777dd9d7058cad45a50e81691bfffe21f Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 00:49:04 -0600 Subject: [PATCH 189/379] Clean up docstrings Indicating string 'left' as ``'left'``, don't use OPTIONAL ARGUMENTS --- .../combinat/fully_commutative_elements.py | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 971baee10f8..5ce6037c65b 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -227,19 +227,17 @@ def heap(self, **kargs): - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. - OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` - and where each element `i` carries `s_i` as its label. The partial order - `\prec` on the poset is defined by declaring `i\prec j` if `i Date: Fri, 14 Aug 2020 00:52:04 -0600 Subject: [PATCH 190/379] Remove periods from input items in docstrings. --- .../combinat/fully_commutative_elements.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 5ce6037c65b..e66146e6017 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -225,13 +225,13 @@ def heap(self, **kargs): INPUT: - - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element. + - ``self`` -- list, a reduced word `w=s_0... s_{k-1}` of an FC element - ``one_index`` -- boolean (default: False). Setting the value to True - will change the underlying set of the poset to `\{1, 2, \dots, n\}`. + will change the underlying set of the poset to `\{1, 2, \dots, n\}` - ``display_labeling`` -- boolean (default: False). Setting the value to - True will display the label `s_i` for each element `i` of the poset. + True will display the label `s_i` for each element `i` of the poset OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` and where each element `i` carries `s_i` as its label. The partial order @@ -378,11 +378,11 @@ def find_descent(self, s, side='left'): INPUT: - - ``s`` -- integer representing a generator of the Coxeter system. + - ``s`` -- integer representing a generator of the Coxeter system - ``side`` -- string (default: ``'left'``); if the argument is set to 'right', the function checks if ``s`` is a right descent of ``self`` - and finds the index of the rightmost occurrence of ``s`` if so. + and finds the index of the rightmost occurrence of ``s`` if so OUTPUT: @@ -420,7 +420,7 @@ def has_descent(self, s, side='left'): INPUT: - ``side`` -- string (default: ``'left'``); if set to 'right', determine - if ``self`` has ``s`` as a right descent. + if ``self`` has ``s`` as a right descent OUTPUT: a boolean value @@ -448,7 +448,7 @@ def descents(self, side='left'): INPUT: - ``side`` -- string (default: ``'left'``); if set to 'right', find the - right descents. + right descents A generator `s` is called a left or right descent of an element `w` if `l(sw)` or `l(ws)` is smaller than `l(w)`, respectively. If `w` is FC, @@ -487,11 +487,11 @@ def coset_decomposition(self, J, side='left'): INPUT: - - ``J`` -- subset of the generating set `S` of the Coxeter system. + - ``J`` -- subset of the generating set `S` of the Coxeter system - ``side`` -- string (default: ``'left'``); if the value is set to 'right', then the function returns the tuple `(w'^J, w'_J)` from the - coset decomposition `w = w'^J \cdot w'_J` of `w` with respect to `J`. + coset decomposition `w = w'^J \cdot w'_J` of `w` with respect to `J` OUTPUT: @@ -559,7 +559,7 @@ def _still_reduced_fc_after_prepending(self, s): INPUT: - - ``s`` -- integer representing a generator of the Coxeter system. + - ``s`` -- integer representing a generator of the Coxeter system - ``self`` -- a reduced word of an FC element EXAMPLES: @@ -693,13 +693,13 @@ def star_operation(self, J, direction, side='left'): INPUT: - ``J`` -- a set of two integers representing two noncommuting - generators of the Coxeter system. + generators of the Coxeter system - ``direction`` -- string, 'upper' or 'lower'; the function performs an - upper or lower star operation according to ``direction``. + upper or lower star operation according to ``direction`` - ``side`` -- string (default: ``'left'``); if this is set to 'right', - the function performs a right star operation. + the function performs a right star operation OUTPUT: From 4f37586cfc8c74bbd4c9166c7ce446ad3032d542 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 00:53:26 -0600 Subject: [PATCH 191/379] Don't break lines in ternary expressions. --- src/sage/combinat/fully_commutative_elements.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index e66146e6017..462b939700c 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -761,14 +761,12 @@ def star_operation(self, J, direction, side='left'): # the upper star operation ending_letter = cur_string[0] if side == 'left' else cur_string[-1] other = next(x for x in J if x != ending_letter) - new_string = [other] + \ - cur_string if side == 'left' else cur_string + [other] + new_string = [other] + cur_string if side == 'left' else cur_string + [other] else: return None # concatenate w_J and w^J in the appropriate order - combined_data = new_string + \ - list(remaining) if side == 'left' else list(remaining) + new_string + combined_data = new_string + list(remaining) if side == 'left' else list(remaining) + new_string # return the result of the star operation in its canonical form return self.parent().element_class(self.parent(), combined_data, check=False) From bc2c7dbee7d2f24a36cfadb632ced82d375abc14 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 01:02:26 -0600 Subject: [PATCH 192/379] Use TestSuite for __init__ doctests. --- src/sage/combinat/fully_commutative_elements.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 462b939700c..1328a487d0e 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -950,12 +950,8 @@ def __init__(self, coxeter_group): EXAMPLES:: sage: from sage.combinat.fully_commutative_elements import FullyCommutativeElements - sage: FullyCommutativeElements(CoxeterGroup(['H', 4])) - Fully commutative elements of Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: - [1 3 2 2] - [3 1 3 2] - [2 3 1 5] - [2 2 5 1] + sage: FC = FullyCommutativeElements(CoxeterGroup(['H', 4])) + sage: TestSuite(FC).run() """ self._coxeter_group = coxeter_group From 34e3d022bd7b341256d6072cdb1ed0dc35366685 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 01:11:58 -0600 Subject: [PATCH 193/379] Clean up formatting on some long-output doctests --- src/sage/combinat/fully_commutative_elements.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 1328a487d0e..42f3d924fb1 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -1085,11 +1085,11 @@ def iterate_to_length(self, length): of an FC element in `A_3`:: sage: list(FCA3.iterate_to_length(4)) - [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], [3, 2, - 1], [2, 1, 3], [1, 3, 2], [1, 2, 3], [2, 1, 3, 2]] + [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], + [3, 2, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3], [2, 1, 3, 2]] sage: list(FCA3.iterate_to_length(5)) - [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], [3, 2, - 1], [2, 1, 3], [1, 3, 2], [1, 2, 3], [2, 1, 3, 2]] + [[], [1], [2], [3], [2, 1], [1, 3], [1, 2], [3, 2], [2, 3], + [3, 2, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3], [2, 1, 3, 2]] sage: list(FCA3.iterate_to_length(4)) == list(FCA3) True @@ -1100,10 +1100,10 @@ def iterate_to_length(self, length): sage: FCAffineA2.category() Category of infinite enumerated sets sage: list(FCAffineA2.iterate_to_length(4)) - [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, - 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 1, - 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], [1, 0, 2, 1], [2, 1, - 0, 2], [2, 0, 1, 2]] + [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], + [1, 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], + [0, 1, 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], + [1, 0, 2, 1], [2, 1, 0, 2], [2, 0, 1, 2]] """ for w in self: if len(w) > length: From df500f4c2b5b1797cc4215dcd84d182f07f73c6e Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 01:16:13 -0600 Subject: [PATCH 194/379] More string literals that should be in code delimiters. --- src/sage/combinat/fully_commutative_elements.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 42f3d924fb1..62673402942 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -695,8 +695,8 @@ def star_operation(self, J, direction, side='left'): - ``J`` -- a set of two integers representing two noncommuting generators of the Coxeter system - - ``direction`` -- string, 'upper' or 'lower'; the function performs an - upper or lower star operation according to ``direction`` + - ``direction`` -- string, ``'upper'`` or ``'lower'``; the function + performs an upper or lower star operation according to ``direction`` - ``side`` -- string (default: ``'left'``); if this is set to 'right', the function performs a right star operation From 89705b006cf8a6e9ae3ee3d95fdca63c7bd17b97 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 01:42:47 -0600 Subject: [PATCH 195/379] Eliminate `cartier_foata_form` and just use `normalize`, instead of redirecting. Remove incorrect statement in `check` that it is simply an alias; move some methods to maintain sectioning. --- .../combinat/fully_commutative_elements.py | 125 ++++++++---------- 1 file changed, 54 insertions(+), 71 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 62673402942..241df1a5a6c 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -53,11 +53,33 @@ class FullyCommutativeElement(NormalizedClonableList): form. See [Gre2006]_. We will normalize each FC element to this form. """ + def group_element(self): + r""" + Get the actual element of the Coxeter group associated with + ``self.parent()`` corresponding to ``self``. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A', 3]) + sage: FC = W.fully_commutative_elements() + sage: x = FC([1, 2]) + sage: x.group_element() + [ 0 -1 1] + [ 1 -1 1] + [ 0 0 1] + sage: x.group_element() in W + True + """ + return self.parent().coxeter_group().from_reduced_word(self) + + ########################################################################### + # Characterization and representation of FC elements # + ########################################################################### + # Methods required as a subclass of NormalizedClonableList: def check(self): r""" - Called automatically when an element is created. Alias of - :func:`is_fully_commutative` + Called automatically when an element is created. TESTS:: @@ -73,38 +95,43 @@ def check(self): def normalize(self): r""" - Called automatically when an element is created. Alias of - :func:`cartier_foata_form` + Return the Cartier--Foata normal form of ``self``. - TESTS:: + EXAMPLES: - sage: CoxeterGroup(['A', 3]).fully_commutative_elements()([3, 1]) # indirect doctest - [1, 3] - """ - return self.cartier_foata_form() + The following reduced words express the same FC elements in `B_5`:: - def group_element(self): - r""" - Get the actual element of the Coxeter group associated with - ``self.parent()`` corresponding to ``self``. + sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() + sage: FC([1, 4, 3, 5, 2, 4, 3]) # indirect doctest + [1, 4, 3, 5, 2, 4, 3] + sage: FC([4, 1, 3, 5, 2, 4, 3]) # indirect doctest + [1, 4, 3, 5, 2, 4, 3] + sage: FC([4, 3, 1, 5, 4, 2, 3]) # indirect doctest + [1, 4, 3, 5, 2, 4, 3] - EXAMPLES:: + .. NOTE:: - sage: W = CoxeterGroup(['A', 3]) - sage: FC = W.fully_commutative_elements() - sage: x = FC([1, 2]) - sage: x.group_element() - [ 0 -1 1] - [ 1 -1 1] - [ 0 0 1] - sage: x.group_element() in W - True + The Cartier--Foata form of a reduced word of an FC element `w` can + be found recursively by repeatedly moving left descents of + elements to the left and ordering the left descents from small to + large. In the above example, the left descents of the element are + 4 and 1, therefore the Cartier--Foata form of the element is the + concatenation of [1,4] with the Cartier--Foata form of the + remaining part of the word. See [Gre2006]_. + + .. SEEALSO:: :func:`descents` """ - return self.parent().coxeter_group().from_reduced_word(self) + self._require_mutable() - ########################################################################### - # Characterization and representation of FC elements # - ########################################################################### + out_word = [] + + while len(self) > 0: + fronts = self.descents() + out_word.extend(sorted(fronts)) + for s in fronts: + self.remove(s) + + self._set_list(out_word) # Full commutativity test def is_fully_commutative(self): @@ -167,50 +194,6 @@ def commute_once(word, i): queue.appendleft(new_word) return True - # Representing FC elements: Canonical forms - def cartier_foata_form(self): - r""" - Return the Cartier--Foata normal form of ``self``. - - :func:`normalize` is an alias of this method, and is called - automatically when an element is created. - - EXAMPLES: - - The following reduced words express the same FC elements in `B_5`:: - - sage: FC = CoxeterGroup(['B', 5]).fully_commutative_elements() - sage: FC([1, 4, 3, 5, 2, 4, 3]) # indirect doctest - [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 1, 3, 5, 2, 4, 3]) # indirect doctest - [1, 4, 3, 5, 2, 4, 3] - sage: FC([4, 3, 1, 5, 4, 2, 3]) # indirect doctest - [1, 4, 3, 5, 2, 4, 3] - - .. NOTE:: - - The Cartier--Foata form of a reduced word of an FC element `w` can - be found recursively by repeatedly moving left descents of - elements to the left and ordering the left descents from small to - large. In the above example, the left descents of the element are - 4 and 1, therefore the Cartier--Foata form of the element is the - concatenation of [1,4] with the Cartier--Foata form of the - remaining part of the word. See [Gre2006]_. - - .. SEEALSO:: :func:`descents` - """ - self._require_mutable() - - out_word = [] - - while len(self) > 0: - fronts = self.descents() - out_word.extend(sorted(fronts)) - for s in fronts: - self.remove(s) - - self._set_list(out_word) - # Representing FC elements: Heaps def heap(self, **kargs): r""" From 5d3a953a8cc268bf22dbd2eff18dfd8afce8ffa4 Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Fri, 14 Aug 2020 02:29:01 -0600 Subject: [PATCH 196/379] More docstring tweaks Use `\ell`, correct in `normalize` docstring. --- src/sage/combinat/fully_commutative_elements.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index 241df1a5a6c..b90ef574e10 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -95,7 +95,7 @@ def check(self): def normalize(self): r""" - Return the Cartier--Foata normal form of ``self``. + Mutate ``self`` into Cartier-Foata normal form. EXAMPLES: @@ -480,9 +480,9 @@ def coset_decomposition(self, J, side='left'): The tuple of elements `(w_J, w^J)` such that `w=w_J \cdot w^J`, `w_J` is generated by the elements in `J`, and `w^J` has no left descent from - `J`. This tuple is unique and satisfies the equation `l(w) = l(w_J) + - l(w^J)`, where `l` denotes Coxeter length, by general theory; see - Proposition 2.4.4 of [BB2005]_. + `J`. This tuple is unique and satisfies the equation `\ell(w) = + \ell(w_J) + \ell(w^J)`, where `\ell` denotes Coxeter length, by general + theory; see Proposition 2.4.4 of [BB2005]_. EXAMPLES:: From 6c7fa3ac3e34dc2a154baf96b5d6cf8ac80cb0f1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 14 Aug 2020 10:21:07 -0700 Subject: [PATCH 197/379] build/pkgs/matplotlib: Update to 3.3.1 --- build/pkgs/matplotlib/checksums.ini | 6 +- build/pkgs/matplotlib/package-version.txt | 2 +- .../fix-path-of-degenerate-polygon.patch | 67 ------------------- 3 files changed, 4 insertions(+), 71 deletions(-) delete mode 100644 build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index eb45b5e3d9c..124c59774bc 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,5 +1,5 @@ tarball=matplotlib-VERSION.tar.gz -sha1=e407249b202d199c704f2bb40bd48776d6a6f633 -md5=b1de7185687c6f5c092689e3431a69b3 -cksum=1389017280 +sha1=2564563937720572f940d14e7718c36c974e9462 +md5=f3a405f340be5b151cb2042c4d8d16f7 +cksum=2092155023 upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz diff --git a/build/pkgs/matplotlib/package-version.txt b/build/pkgs/matplotlib/package-version.txt index 08be6eec65b..bea438e9ade 100644 --- a/build/pkgs/matplotlib/package-version.txt +++ b/build/pkgs/matplotlib/package-version.txt @@ -1 +1 @@ -3.3.0.p0 +3.3.1 diff --git a/build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch b/build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch deleted file mode 100644 index bedd4a48f08..00000000000 --- a/build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 07847dd27dd16296999161a616ea27445c5a49db Mon Sep 17 00:00:00 2001 -From: Elliott Sales de Andrade -Date: Tue, 21 Jul 2020 20:11:50 -0400 -Subject: [PATCH] Backport PR #17982: BF: for degenerate polygons, add - CLOSEPOLY vertex - ---- - lib/matplotlib/patches.py | 18 ++++++++++++++++-- - lib/matplotlib/tests/test_patches.py | 7 +++++++ - 2 files changed, 23 insertions(+), 2 deletions(-) - -diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py -index 5811cba39ad..fdd06f004f7 100644 ---- a/lib/matplotlib/patches.py -+++ b/lib/matplotlib/patches.py -@@ -1063,13 +1063,27 @@ def set_xy(self, xy): - ---------- - xy : (N, 2) array-like - The coordinates of the vertices. -+ -+ Notes -+ ----- -+ Unlike `~.path.Path`, we do not ignore the last input vertex. If the -+ polygon is meant to be closed, and the last point of the polygon is not -+ equal to the first, we assume that the user has not explicitly passed a -+ ``CLOSEPOLY`` vertex, and add it ourselves. - """ - xy = np.asarray(xy) -+ nverts, _ = xy.shape - if self._closed: -- if len(xy) and (xy[0] != xy[-1]).any(): -+ # if the first and last vertex are the "same", then we assume that -+ # the user explicitly passed the CLOSEPOLY vertex. Otherwise, we -+ # have to append one since the last vertex will be "ignored" by -+ # Path -+ if nverts == 1 or nverts > 1 and (xy[0] != xy[-1]).any(): - xy = np.concatenate([xy, [xy[0]]]) - else: -- if len(xy) > 2 and (xy[0] == xy[-1]).all(): -+ # if we aren't closed, and the last vertex matches the first, then -+ # we assume we have an unecessary CLOSEPOLY vertex and remove it -+ if nverts > 2 and (xy[0] == xy[-1]).all(): - xy = xy[:-1] - self._path = Path(xy, closed=self._closed) - self.stale = True -diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py -index 475300b7c2d..3b9d1e0adb3 100644 ---- a/lib/matplotlib/tests/test_patches.py -+++ b/lib/matplotlib/tests/test_patches.py -@@ -7,6 +7,7 @@ - - from matplotlib.patches import Polygon, Rectangle, FancyArrowPatch - from matplotlib.testing.decorators import image_comparison, check_figures_equal -+from matplotlib.transforms import Bbox - import matplotlib.pyplot as plt - from matplotlib import ( - collections as mcollections, colors as mcolors, patches as mpatches, -@@ -556,3 +557,9 @@ def test_rotated_arcs(): - ax.axvline(0, color="k") - ax.set_axis_off() - ax.set_aspect("equal") -+ -+ -+def test_degenerate_polygon(): -+ point = [0, 0] -+ correct_extents = Bbox([point, point]).extents -+ assert np.all(Polygon([point]).get_extents().extents == correct_extents) From a4c2524329bb4394cc36f0f487bfa2dcb1f1094d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 14 Aug 2020 13:59:17 -0700 Subject: [PATCH 198/379] build/pkgs/certifi: Update to 2020.6.20 --- build/pkgs/certifi/checksums.ini | 7 ++++--- build/pkgs/certifi/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/certifi/checksums.ini b/build/pkgs/certifi/checksums.ini index 52c288612ef..27a399a1c20 100644 --- a/build/pkgs/certifi/checksums.ini +++ b/build/pkgs/certifi/checksums.ini @@ -1,4 +1,5 @@ tarball=certifi-VERSION.tar.gz -sha1=f3873edcfc60c52e97e6601b2576ccdac419281a -md5=76381d19d0a1171fecb2d1002b81424e -cksum=2417220143 +sha1=4bb6fad4f43f262f61d306581d9e29c91ff1d1e2 +md5=89525ece725d674d91b0c43007d2a47e +cksum=4236975362 +upstream_url=https://pypi.io/packages/source/c/certifi/certifi-VERSION.tar.gz diff --git a/build/pkgs/certifi/package-version.txt b/build/pkgs/certifi/package-version.txt index bfcfddae80a..c823a422ae3 100644 --- a/build/pkgs/certifi/package-version.txt +++ b/build/pkgs/certifi/package-version.txt @@ -1 +1 @@ -2019.3.9 +2020.6.20 From d8e4aab73f5389a629c06775ba76667b77c1bcc7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 14 Aug 2020 14:07:23 -0700 Subject: [PATCH 199/379] build/pkgs/matplotlib/dependencies: Add certifi --- build/pkgs/matplotlib/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index 1b838fdc905..542dab4e297 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler | $(PYTHON_TOOLCHAIN) pytz kiwisolver +$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler | $(PYTHON_TOOLCHAIN) pytz kiwisolver certifi ---------- All lines of this file are ignored except the first. From b2f04334c89040c5fb80b2be2be366dedc497d2e Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Sat, 15 Aug 2020 17:36:14 +0200 Subject: [PATCH 200/379] 29243: improve error handling --- src/sage/matrix/matrix2.pyx | 82 ++++++++++++++++++----- src/sage/matrix/matrix_symbolic_dense.pyx | 31 ++++++++- 2 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index c02322847d2..e2d0dca285e 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -6038,10 +6038,18 @@ cdef class Matrix(Matrix1): self.cache('eigenvalues', eigenvalues) return eigenvalues - def eigenvectors_left(self,extend=True): + def eigenvectors_left(self, other=None, extend=True): r""" Compute the left eigenvectors of a matrix. + INPUT: + + - ``other`` -- not supported + + - ``extend`` -- boolean (default: ``True``) + + OUTPUT: + For each distinct eigenvalue, returns a list of the form (e,V,n) where e is the eigenvalue, V is a list of eigenvectors forming a basis for the corresponding left eigenspace, and n is the algebraic @@ -6050,8 +6058,9 @@ cdef class Matrix(Matrix1): If the option extend is set to False, then only the eigenvalues that live in the base ring are considered. - EXAMPLES: We compute the left eigenvectors of a `3\times 3` - rational matrix. + EXAMPLES: + + We compute the left eigenvectors of a `3\times 3` rational matrix. :: @@ -6084,7 +6093,36 @@ cdef class Matrix(Matrix1): (0, 0, 1) ], 1)] + TESTS:: + + sage: A = matrix(QQ, [[1, 2], [3, 4]]) + sage: B = matrix(QQ, [[1, 1], [0, 1]]) + sage: A.eigenvectors_left(B) + Traceback (most recent call last): + ... + NotImplementedError: generalized eigenvector decomposition is + implemented for RDF and CDF, but not for Rational Field + + Check the deprecation:: + + sage: matrix(QQ, [[1, 2], [3, 4]]).eigenvectors_left(False) + doctest:...: DeprecationWarning: "extend" should be used as keyword argument + See https://trac.sagemath.org/29243 for details. + [] """ + if other is not None: + if isinstance(other, bool): + # for backward compatibility + from sage.misc.superseded import deprecation + deprecation(29243, + '"extend" should be used as keyword argument') + extend = other + else: + raise NotImplementedError('generalized eigenvector ' + 'decomposition is implemented ' + 'for RDF and CDF, but not for %s' + % self.base_ring()) + x = self.fetch('eigenvectors_left') if not x is None: return x @@ -6124,10 +6162,18 @@ cdef class Matrix(Matrix1): left_eigenvectors = eigenvectors_left - def eigenvectors_right(self, extend=True): + def eigenvectors_right(self, other=None, extend=True): r""" Compute the right eigenvectors of a matrix. + INPUT: + + - ``other`` -- not supported + + - ``extend`` -- boolean (default: ``True``) + + OUTPUT: + For each distinct eigenvalue, returns a list of the form (e,V,n) where e is the eigenvalue, V is a list of eigenvectors forming a basis for the corresponding right eigenspace, and n is the @@ -6136,8 +6182,9 @@ cdef class Matrix(Matrix1): closure of the base field where this is implemented; otherwise it will restrict to eigenvalues in the base field. - EXAMPLES: We compute the right eigenvectors of a - `3\times 3` rational matrix. + EXAMPLES: + + We compute the right eigenvectors of a `3\times 3` rational matrix. :: @@ -6159,8 +6206,18 @@ cdef class Matrix(Matrix1): sage: delta = eval*evec - A*evec sage: abs(abs(delta)) < 1e-10 True + + TESTS:: + + sage: A = matrix(QQ, [[1, 2], [3, 4]]) + sage: B = matrix(QQ, [[1, 1], [0, 1]]) + sage: A.eigenvectors_right(B) + Traceback (most recent call last): + ... + NotImplementedError: generalized eigenvector decomposition is + implemented for RDF and CDF, but not for Rational Field """ - return self.transpose().eigenvectors_left(extend=extend) + return self.transpose().eigenvectors_left(other=other, extend=extend) right_eigenvectors = eigenvectors_right @@ -6327,16 +6384,7 @@ cdef class Matrix(Matrix1): """ from sage.misc.flatten import flatten from sage.matrix.constructor import diagonal_matrix, matrix - if other is None: - evecs = self.eigenvectors_left() - else: - try: - evecs = self.eigenvectors_left(other=other) - except TypeError as e: - raise NotImplementedError('generalized eigenvector ' - 'decomposition is implemented ' - 'for RDF and CDF, but not for %s' - % self.base_ring()) from e + evecs = self.eigenvectors_left(other=other) D = diagonal_matrix(flatten([[e[0]]*e[2] for e in evecs])) rows = [] for e in evecs: diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 2065a61e844..b00a41a6a7b 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -183,7 +183,7 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): raise ArithmeticError("could not determine eigenvalues exactly using symbolic matrices; try using a different type of matrix via self.change_ring(), if possible") return sum([[ev] * int(mult) for ev, mult in zip(*maxima_evals)], []) - def eigenvectors_left(self): + def eigenvectors_left(self, other=None): r""" Compute the left eigenvectors of a matrix. @@ -256,7 +256,22 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): sage: am = G.adjacency_matrix().change_ring(SR) sage: am.eigenvectors_left() [(-1, [(1, 0, -1, 1, 0, -1), (0, 1, -1, 0, 1, -1)], 2), (1, [(1, 0, -1, -1, 0, 1), (0, 1, 1, 0, -1, -1)], 2), (-2, [(1, -1, 1, -1, 1, -1)], 1), (2, [(1, 1, 1, 1, 1, 1)], 1)] + + TESTS:: + + sage: A = matrix(SR, [[1, 2], [3, 4]]) + sage: B = matrix(SR, [[1, 1], [0, 1]]) + sage: A.eigenvectors_left(B) + Traceback (most recent call last): + ... + NotImplementedError: generalized eigenvector decomposition is + implemented for RDF and CDF, but not for Symbolic Ring """ + if other is not None: + raise NotImplementedError('generalized eigenvector decomposition ' + 'is implemented for RDF and CDF, but ' + 'not for %s' % self.base_ring()) + from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ @@ -267,7 +282,7 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): return result - def eigenvectors_right(self): + def eigenvectors_right(self, other=None): r""" Compute the right eigenvectors of a matrix. @@ -291,8 +306,18 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): [(-1/2*sqrt(17) + 3/2, [(1, -1/2*sqrt(17) + 3/2)], 1), (1/2*sqrt(17) + 3/2, [(1, 1/2*sqrt(17) + 3/2)], 1)] sage: right[0][1] == left[0][1] True + + TESTS:: + + sage: A = matrix(SR, [[1, 2], [3, 4]]) + sage: B = matrix(SR, [[1, 1], [0, 1]]) + sage: A.eigenvectors_right(B) + Traceback (most recent call last): + ... + NotImplementedError: generalized eigenvector decomposition is + implemented for RDF and CDF, but not for Symbolic Ring """ - return self.transpose().eigenvectors_left() + return self.transpose().eigenvectors_left(other=other) def exp(self): r""" From fc48d6d937fd497268518d8ee8f153918ff4dde4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Aug 2020 12:08:55 -0700 Subject: [PATCH 201/379] sage.categories.pushout.AlgebraicExtensionFunctor: Handle latex_names --- src/sage/categories/pushout.py | 46 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 0f2753185b4..59078f20879 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2901,7 +2901,8 @@ class AlgebraicExtensionFunctor(ConstructionFunctor): rank = 3 def __init__(self, polys, names, embeddings=None, structures=None, - cyclotomic=None, precs=None, implementations=None, **kwds): + cyclotomic=None, precs=None, implementations=None, + *, latex_names=None, **kwds): """ INPUT: @@ -2931,6 +2932,9 @@ def __init__(self, polys, names, embeddings=None, structures=None, If it is provided, it is used to determine an implementation in the p-adic case. + - ``latex_names`` -- (optional) list of strings of the same length + as the list ``polys`` + - ``**kwds`` -- further keywords; when the functor is applied to a ring `R`, these are passed to the ``extension()`` method of `R`. @@ -3018,7 +3022,9 @@ def __init__(self, polys, names, embeddings=None, structures=None, precs = [None] * n if implementations is None: implementations = [None] * n - if not (len(names) == len(embeddings) == len(structures) == n): + if latex_names is None: + latex_names = [None] * n + if not (len(names) == len(embeddings) == len(structures) == len(latex_names) == n): raise ValueError("All arguments must be of the same length") self.polys = list(polys) self.names = list(names) @@ -3027,6 +3033,14 @@ def __init__(self, polys, names, embeddings=None, structures=None, self.cyclotomic = int(cyclotomic) if cyclotomic is not None else None self.precs = list(precs) self.implementations = list(implementations) + # Normalize latex_names: Use None when latex_name does not override the default. + latex_names = list(latex_names) + for i, name in enumerate(self.names): + if latex_names[i] is not None: + from sage.misc.latex import latex_variable_name + if latex_names[i] == latex_variable_name(name): + latex_names[i] = None + self.latex_names = latex_names self.kwds = kwds def _apply_functor(self, R): @@ -3062,19 +3076,34 @@ def _apply_functor(self, R): if len(self.polys) == 1: return R.extension(self.polys[0], names=self.names[0], embedding=self.embeddings[0], structure=self.structures[0], prec=self.precs[0], - implementation=self.implementations[0], **self.kwds) + implementation=self.implementations[0], + latex_name=self.latex_names[0], **self.kwds) return R.extension(self.polys, names=self.names, embedding=self.embeddings, structure=self.structures, prec=self.precs, - implementation=self.implementations, **self.kwds) + implementation=self.implementations, + latex_name=self.latex_names, **self.kwds) def __eq__(self, other): """ + Check whether ``self`` is equal to ``other``. + TESTS:: sage: K.=NumberField(x^3+x^2+1) sage: F = K.construction()[0] sage: F == loads(dumps(F)) True + + sage: K2. = NumberField(x^3+x^2+1, latex_name='a') + sage: F2 = K2.construction()[0] + sage: F2 == F + True + + sage: K3. = NumberField(x^3+x^2+1, latex_name='alpha') + sage: F3 = K3.construction()[0] + sage: F3 == F + False + """ if not isinstance(other, AlgebraicExtensionFunctor): return False @@ -3082,7 +3111,8 @@ def __eq__(self, other): return (self.polys == other.polys and self.embeddings == other.embeddings and self.structures == other.structures and - self.precs == other.precs) + self.precs == other.precs and + self.latex_names == other.latex_names) def __ne__(self, other): """ @@ -3234,7 +3264,7 @@ def merge(self, other): def __mul__(self, other): """ - Compose construction functors to a composit construction functor, unless one of them is the identity. + Compose construction functors to a composite construction functor, unless one of them is the identity. .. NOTE:: @@ -3261,6 +3291,7 @@ def __mul__(self, other): self.structures + other.structures, precs=self.precs + other.precs, implementations=self.implementations + other.implementations, + latex_names=self.latex_names + other.latex_names, **self.kwds) elif (isinstance(other, CompositeConstructionFunctor) and isinstance(other.all[-1], AlgebraicExtensionFunctor)): @@ -3294,7 +3325,8 @@ def expand(self): return [self] return [AlgebraicExtensionFunctor([self.polys[i]], [self.names[i]], [self.embeddings[i]], [self.structures[i]], precs=[self.precs[i]], - implementations=[self.implementations[i]], **self.kwds) + implementations=[self.implementations[i]], + latex_names=[self.latex_names[i]], **self.kwds) for i in range(n)] From d85068ffe6353790ea03d3dcd1cb652883a78923 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Aug 2020 12:11:00 -0700 Subject: [PATCH 202/379] CommutativeRing.extension, NumberField_generic.extension, FiniteField.extension: Accept latex_name argument --- src/sage/rings/finite_rings/finite_field_base.pyx | 3 ++- src/sage/rings/finite_rings/finite_field_constructor.py | 2 +- src/sage/rings/number_field/number_field.py | 4 ++-- src/sage/rings/ring.pyx | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 8ff05b1b4d0..d1cebcb0580 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1349,7 +1349,8 @@ cdef class FiniteField(Field): return (AlgebraicExtensionFunctor([self.polynomial()], [self.variable_name()], [None]), self.base_ring()) - def extension(self, modulus, name=None, names=None, map=False, embedding=None, **kwds): + def extension(self, modulus, name=None, names=None, map=False, embedding=None, + latex_name=None, **kwds): """ Return an extension of this finite field. diff --git a/src/sage/rings/finite_rings/finite_field_constructor.py b/src/sage/rings/finite_rings/finite_field_constructor.py index 4bab314b521..80cf269cc08 100644 --- a/src/sage/rings/finite_rings/finite_field_constructor.py +++ b/src/sage/rings/finite_rings/finite_field_constructor.py @@ -519,7 +519,7 @@ def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, if proof is None: proof = arithmetic() for key, val in kwds.items(): - if key not in ['structure', 'implementation', 'prec', 'embedding']: + if key not in ['structure', 'implementation', 'prec', 'embedding', 'latex_names']: raise TypeError("create_key_and_extra_args() got an unexpected keyword argument '%s'"%key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): raise NotImplementedError("ring extension with prescribed %s is not implemented"%key) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 11b39ea5c1c..ec6fe54446e 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -5376,7 +5376,7 @@ def elements_of_norm(self, n, proof=None): B = self.pari_bnf(proof).bnfisintnorm(n) return [self(x, check=False) for x in B] - def extension(self, poly, name=None, names=None, *args, **kwds): + def extension(self, poly, name=None, names=None, latex_name=None, *args, **kwds): """ Return the relative extension of this field by a given polynomial. @@ -5427,7 +5427,7 @@ def extension(self, poly, name=None, names=None, *args, **kwds): name = names if isinstance(name, tuple): name = name[0] - return NumberField(poly, name, *args, **kwds) + return NumberField(poly, name, latex_name=latex_name, *args, **kwds) def factor(self, n): r""" diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index da0feacb56c..a8bcf226c41 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -1508,7 +1508,7 @@ cdef class CommutativeRing(Ring): if name is None: name = str(poly.parent().gen(0)) for key, val in kwds.items(): - if key not in ['structure', 'implementation', 'prec', 'embedding']: + if key not in ['structure', 'implementation', 'prec', 'embedding', 'latex_name']: raise TypeError("extension() got an unexpected keyword argument '%s'"%key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): raise NotImplementedError("ring extension with prescripted %s is not implemented"%key) From 7a88492013d4b4147c35d0ceb2124e38bc393aae Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Aug 2020 12:11:34 -0700 Subject: [PATCH 203/379] NumberField_generic.construction: Pass latex_names to AlgebraicExtensionFunctor --- src/sage/rings/number_field/number_field.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index ec6fe54446e..9aad97d74e7 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -1540,10 +1540,18 @@ def construction(self): sage: a*b a*b + The construction preserves latex variable names:: + + sage: K. = NumberField([x^3+x^2+1, x^2+1, x^7+x+1], latex_name=['alpha', 'beta', 'gamma']) + sage: F, R = K.construction() + sage: F(R) == K + True + """ from sage.categories.pushout import AlgebraicExtensionFunctor from sage.all import QQ names = self.variable_names() + latex_names = self.latex_variable_names() polys = [] embeddings = [] structures = [] @@ -1553,7 +1561,8 @@ def construction(self): embeddings.append(None if K.coerce_embedding() is None else K.coerce_embedding()(K.gen())) structures.append(K._structure) K = K.base_field() - return (AlgebraicExtensionFunctor(polys, names, embeddings, structures), QQ) + return (AlgebraicExtensionFunctor(polys, names, embeddings, structures, + latex_names=latex_names), QQ) def _element_constructor_(self, x, check=True): r""" From 56f6c4d754634cd764d0e9be1906047afa239456 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Aug 2020 13:21:49 -0700 Subject: [PATCH 204/379] sage.rings.number_field.number_field.NumberFieldTower: Pass latex_name to extension --- src/sage/rings/number_field/number_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 30793859933..983deeadfc3 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -825,7 +825,7 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name var = f.variable_name() if is_Polynomial(f) else 'x' R = w[var] # polynomial ring - return w.extension(R(f), name, check=check, embedding=embeddings[0], structure=structures[0]) # currently, extension does not accept assume_disc_small, or maximize_at_primes + return w.extension(R(f), name, check=check, embedding=embeddings[0], structure=structures[0], latex_name=latex_names[0]) # currently, extension does not accept assume_disc_small, or maximize_at_primes def QuadraticField(D, name='a', check=True, embedding=True, latex_name='sqrt', **args): r""" From 42eb272ad538d404e7375fb5e0560e4e2ccf6508 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Aug 2020 13:22:35 -0700 Subject: [PATCH 205/379] sage.rings.number_field.number_field.NumberField_generic.construction: Collect latex_names from the tower of extensions --- src/sage/rings/number_field/number_field.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 983deeadfc3..bc7bc4f5ed1 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -1552,15 +1552,16 @@ def construction(self): from sage.categories.pushout import AlgebraicExtensionFunctor from sage.all import QQ names = self.variable_names() - latex_names = self.latex_variable_names() polys = [] embeddings = [] structures = [] + latex_names = [] K = self while K is not QQ: polys.append(K.relative_polynomial()) embeddings.append(None if K.coerce_embedding() is None else K.coerce_embedding()(K.gen())) structures.append(K._structure) + latex_names.append(K.latex_variable_names()[0]) K = K.base_field() return (AlgebraicExtensionFunctor(polys, names, embeddings, structures, latex_names=latex_names), QQ) From 20ce24899b75292cec183f6c6f8c4af8c13fbcfc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Aug 2020 13:26:54 -0700 Subject: [PATCH 206/379] src/sage/rings/number_field/number_field_element_quadratic.pyx: Add test for #30360 --- .../number_field/number_field_element_quadratic.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 61bb0b99d47..9583a2c20ba 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -1442,6 +1442,15 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: K. = NumberField(x^2-41) sage: (10^1000 * (a+1)) * K(2+3*a) == 10^1000 * ((a+1) * K(2+3*a)) True + + TESTS: + + Test that :trac:`30360` is fixed:: + + sage: K. = QuadraticField(5, embedding=AA(5).sqrt()) + sage: sqrt5*vector([1,2]) + (sqrt5, 2*sqrt5) + """ cdef NumberFieldElement_quadratic other = other_m cdef NumberFieldElement_quadratic res = self._new() From 8206e2de402b7af66bbaa99045069ac957a4314e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 16 Aug 2020 16:35:54 +0200 Subject: [PATCH 207/379] trac #30377: improve generic_graph.py --- src/sage/graphs/generic_graph.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 409a8a24e2b..2a255a7e0e0 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -443,6 +443,18 @@ def igraph_feature(): + """ + Helper method to check whether optional package ``igraph`` is installed. + + TESTS:: + + sage: from sage.graphs.generic_graph import igraph_feature + sage: ((igraph_feature().is_present() + ....: and 'igraph' in installed_packages()) + ....: or (not igraph_feature().is_present() + ....: and 'igraph' not in installed_packages())) + True + """ from sage.features import PythonModule return PythonModule("igraph", spkg="python_igraph", url="http://igraph.org") @@ -476,13 +488,19 @@ def __init__(self): """ self._latex_opts = None - def __setstate__(self,state): + def __setstate__(self, state): r""" Set the state from a pickle dict - Also converts old NetworkX backends into a more recent one. + TESTS:: + + sage: G = graphs.PetersenGraph() + sage: s = dumps(G) + sage: H = loads(s) + sage: all(k in H.__dict__ for k in G.__dict__.keys()) + True """ - for k,v in state.items(): + for k, v in state.items(): self.__dict__[k] = v def __add__(self, other): From 6883f31fc50a7b80dc1adac56f9e846d8456479d Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 16 Aug 2020 19:05:49 +0200 Subject: [PATCH 208/379] trac #30377: graph_list.py and fast_digraph.pyx --- .../graph_decompositions/fast_digraph.pxd | 1 + .../graph_decompositions/fast_digraph.pyx | 126 +++++++++++++----- src/sage/graphs/graph_list.py | 14 ++ 3 files changed, 108 insertions(+), 33 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/fast_digraph.pxd b/src/sage/graphs/graph_decompositions/fast_digraph.pxd index d9bf894df66..eee5267ecd3 100644 --- a/src/sage/graphs/graph_decompositions/fast_digraph.pxd +++ b/src/sage/graphs/graph_decompositions/fast_digraph.pxd @@ -9,5 +9,6 @@ cdef class FastDigraph: cdef int compute_out_neighborhood_cardinality(FastDigraph, int) cdef int popcount32(int) +cdef int slow_popcount32(int) diff --git a/src/sage/graphs/graph_decompositions/fast_digraph.pyx b/src/sage/graphs/graph_decompositions/fast_digraph.pyx index 1cb012dffd7..33ee68daf6a 100644 --- a/src/sage/graphs/graph_decompositions/fast_digraph.pyx +++ b/src/sage/graphs/graph_decompositions/fast_digraph.pyx @@ -32,6 +32,18 @@ cdef class FastDigraph: - ``vertex_list`` -- list (default: ``None``); specifies a mapping between `[0..n-1]` and the set of vertices of the input (Di)Graph, ``list(D)`` by default + + TESTS:: + + sage: cython_code = [ + ....: 'from sage.graphs.graph import Graph', + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph', + ....: 'G = Graph([(0, 1), (1, 2)])', + ....: 'cdef FastDigraph F = FastDigraph(G)', + ....: 'cdef int i', + ....: 'print([F.degree[i] for i in range(F.n)])'] + sage: cython(os.linesep.join(cython_code)) + [1, 2, 1] """ if D.order() > 8*sizeof(int): raise OverflowError("Too many vertices. This structure can only encode digraphs on at most %i vertices"%(8*sizeof(int))) @@ -78,10 +90,21 @@ cdef class FastDigraph: def print_adjacency_matrix(self): r""" Displays the adjacency matrix of ``self``. + + TESTS:: + + sage: cython_code = [ + ....: 'from sage.graphs.graph import Graph', + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph', + ....: 'FastDigraph(Graph([(0, 1), (1, 2)])).print_adjacency_matrix()'] + sage: cython(os.linesep.join(cython_code)) + 010 + 101 + 010 """ - cdef int i,j - for 0<= i>j)&1), end="") print("") @@ -91,8 +114,20 @@ cdef inline int compute_out_neighborhood_cardinality(FastDigraph g, int S): INPUT: - - ``g`` a FastDigraph - - S (integer) an integer describing the set + - ``g`` -- a FastDigraph + - ``S`` -- an integer describing the set + + TESTS:: + + sage: cython_code = [ + ....: 'from sage.graphs.graph import Graph', + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph', + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport compute_out_neighborhood_cardinality', + ....: 'cdef FastDigraph F = FastDigraph(Graph([(0, 1), (1, 2)]))', + ....: 'cdef int i', + ....: 'print([compute_out_neighborhood_cardinality(F, 1< 4, this function only returns the number of '1' + bits in (i & ((1<<32) - 1)). - If sizeof(int) > 4, this function only returns the number of '1' - bits in (i & ((1<<32) - 1)). - """ - i = i - ((i >> 1) & 0x55555555); - i = (i & 0x33333333) + ((i >> 2) & 0x33333333); - return ((i + (i >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24; + TESTS:: + + sage: cython_code = [ + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport popcount32', + ....: 'cdef int i', + ....: 'print([popcount32(i) for i in range(16)])'] + sage: cython(os.linesep.join(cython_code)) + [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4] + """ + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + return ((i + (i >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24; # If you happened to be doubting the consistency of the popcount32 function @@ -120,30 +164,46 @@ cdef inline int popcount32(int i): # it would report any problem if it finds one. def test_popcount(): - """ - Correction test for popcount32. + """ + Correction test for popcount32. - EXAMPLES:: + EXAMPLES:: - sage: from sage.graphs.graph_decompositions.fast_digraph import test_popcount - sage: test_popcount() # not tested - """ - cdef int i = 1 - # While the last 32 bits of i are not equal to 0 - while (i & ((1<<32) - 1)) : - if popcount32(i) != slow_popcount32(i): - print("Error for i = ", str(i)) - print("Result with popcount32 : " + str(popcount32(i))) - print("Result with slow_popcount32 : " + str(slow_popcount32(i))) - i += 1 + sage: from sage.graphs.graph_decompositions.fast_digraph import test_popcount + sage: test_popcount() # not tested + """ + cdef int i = 1 + # While the last 32 bits of i are not equal to 0 + while (i & ((1<<32) - 1)) : + if popcount32(i) != slow_popcount32(i): + print("Error for i = ", str(i)) + print("Result with popcount32 : " + str(popcount32(i))) + print("Result with slow_popcount32 : " + str(slow_popcount32(i))) + i += 1 cdef inline int slow_popcount32(int i): - # Slow popcount for 32bits integers - cdef int j = 0 - cdef int k + """ + Return the number of '1' bits in a 32-bits integer. + + If sizeof(int) > 4, this function only returns the number of '1' + bits in (i & ((1<<32) - 1)). + + TESTS:: + + sage: cython_code = [ + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport popcount32', + ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport slow_popcount32', + ....: 'cdef int i', + ....: 'print(all(popcount32(i) == slow_popcount32(i) for i in range(16)))'] + sage: cython(os.linesep.join(cython_code)) + True + """ + # Slow popcount for 32bits integers + cdef int j = 0 + cdef int k - for k in range(32): - j += (i>>k) & 1 + for k in range(32): + j += (i>>k) & 1 - return j + return j diff --git a/src/sage/graphs/graph_list.py b/src/sage/graphs/graph_list.py index 376a5f7eabf..a1d61625aa1 100644 --- a/src/sage/graphs/graph_list.py +++ b/src/sage/graphs/graph_list.py @@ -65,6 +65,12 @@ def _from_whatever(data, fmt=None): either ``'graph6'``, ``sparse6``, or ``None``, with the latter case indicating that the ``Graph`` constructor should determine this for itself + + EXAMPLES:: + + sage: l = ['N@@?N@UGAGG?gGlKCMO', ':P_`cBaC_ACd`C_@BC`ABDHaEH_@BF_@CHIK_@BCEHKL_BIKM_BFGHI'] + sage: graphs_list.from_whatever(l) + [Graph on 15 vertices, Looped multi-graph on 17 vertices] """ from sage.graphs.graph import Graph @@ -202,6 +208,14 @@ def to_sparse6(graphs, file=None, output_list=False): def _to_graph6(graphs, file=None, output_list=False, sparse=False): """ Internal implementation of :func:`to_graph6` and :func:`to_sparse6`. + + EXAMPLES:: + + sage: l = [graphs.DodecahedralGraph(), graphs.PetersenGraph()] + sage: graphs_list._to_graph6(l, sparse=False) + 'ShCHGD@?K?_@?@?C_GGG@??cG?G?GK_?C\nIheA@GUAo\n' + sage: graphs_list._to_graph6(l, sparse=True) + ':S_`abcaDe`Fg_HijhKfLdMkNcOjP_BQ\n:I`ES@obGkqegW~\n' """ if sparse: method = 'sparse6_string' From 1a4386aec78ad8582a7ef9d25a7cf9c791f678b4 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 16 Aug 2020 19:24:16 +0200 Subject: [PATCH 209/379] trac #30377: tutte_polynomial.py --- src/sage/graphs/tutte_polynomial.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py index 377d6b253b8..f01a21437f9 100644 --- a/src/sage/graphs/tutte_polynomial.py +++ b/src/sage/graphs/tutte_polynomial.py @@ -472,8 +472,15 @@ def _cache_key(G): Return the key used to cache the result for the graph G This is used by the decorator :func:`_cached`. + + EXAMPLES:: + + sage: from sage.graphs.tutte_polynomial import _cache_key + sage: G = graphs.DiamondGraph() + sage: print(_cache_key(G)) + ((0, 2), (0, 3), (1, 2), (1, 3), (2, 3)) """ - return tuple(sorted(G.canonical_label().edges(labels=False))) + return tuple(G.canonical_label().edges(labels=False, sort=True)) def _cached(func): From 7525eaafe3f4101edcbc4278d9c4873ab32ea221 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 16 Aug 2020 16:21:06 -0400 Subject: [PATCH 210/379] modified and optimized is_path. added a degree filter to vertices and vertex_iterator in graph.py` ` --- src/sage/graphs/generic_graph.py | 25 ++++++++++++++++++---- src/sage/graphs/graph.py | 36 ++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 3a130d5bdc9..bba2e3d4ab1 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10417,7 +10417,7 @@ def get_vertices(self, verts=None): return {v: self._assoc.get(v, None) for v in verts} - def vertex_iterator(self, vertices=None): + def vertex_iterator(self, vertices=None, degree=None): """ Return an iterator over the given vertices. @@ -10430,6 +10430,9 @@ def vertex_iterator(self, vertices=None): - ``vertices`` -- iterated vertices are these intersected with the vertices of the (di)graph + - ``degree`` -- a nonnegative integer (default: ``None``) that specifies + vertices with the given degree + EXAMPLES:: sage: P = graphs.PetersenGraph() @@ -10452,6 +10455,14 @@ def vertex_iterator(self, vertices=None): 2 3 + :: + + sage: H = graphs.PathGraph(5) + sage: for v in H.vertex_iterator(degree=1): + ....: print(v) + 0 + 4 + Note that since the intersection option is available, the vertex_iterator() function is sub-optimal, speed-wise, but note the following optimization:: @@ -10461,6 +10472,9 @@ def vertex_iterator(self, vertices=None): sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop """ + if degree: + return [v for v, d in self.degree_iterator(labels=True) if d == degree] + return self._backend.iterator_verts(vertices) __iter__ = vertex_iterator @@ -10537,7 +10551,7 @@ def neighbor_iterator(self, vertex, closed=False): for u in self._backend.iterator_nbrs(vertex): yield u - def vertices(self, sort=True, key=None): + def vertices(self, sort=True, key=None, degree=None): r""" Return a list of the vertices. @@ -10550,6 +10564,9 @@ def vertices(self, sort=True, key=None): vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have ``sort=True``) + - ``degree`` -- a nonnegative integer (default: ``None``); an integer + that specifies vertices only with the given degree + OUTPUT: The list of vertices of the (di)graph. @@ -10627,8 +10644,8 @@ def vertices(self, sort=True, key=None): if (not sort) and key: raise ValueError('sort keyword is False, yet a key function is given') if sort: - return sorted(self.vertex_iterator(), key=key) - return list(self.vertex_iterator()) + return sorted(self.vertex_iterator(degree=degree), key=key) + return list(self.vertex_iterator(degree=degree)) def neighbors(self, vertex, closed=False): """ diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 92b6215748c..d3bc6f09a92 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3244,22 +3244,40 @@ def is_path(self): sage: H = graphs.CycleGraph(5) sage: H.is_path() False + sage: D = graphs.PathGraph(5).disjoint_union(graphs.CycleGraph(5)) + sage: D.is_path() + False + sage: E = graphs.EmptyGraph() + sage: E.is_path() + False + sage: O = Graph([[1], []]) + sage: O.is_path() + True + sage: O.allow_loops(True) + sage: O.add_edge(1, 1) + sage: O.is_path() + False """ + order = self.order() + if order != self.size() + 1: + return False + + if order <= 1: + return True if order else False + deg_one_counter = 0 seen_counter = 0 - for v in self.depth_first_search(self.vertices()[0]): + for v in self.depth_first_search(next(self.vertex_iterator())): seen_counter += 1 - - if deg_one_counter > 2: - return False - - if self.degree(v) == 1: + deg = self._backend.degree(v, False) + if deg == 1: deg_one_counter += 1 + if deg_one_counter > 2: + return False - elif self.degree(v) != 2: + elif deg != 2: return False - - return seen_counter == self.order() and deg_one_counter == 2 + return deg_one_counter == 2 and seen_counter == order @doc_index("Connectivity, orientations, trees") From a29ada50c752023772e306147af29fedd7791285 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 16 Aug 2020 16:24:20 -0400 Subject: [PATCH 211/379] removed vertices_with_degree function --- src/sage/graphs/generic_graph.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index bba2e3d4ab1..e70ffc3d53d 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12101,30 +12101,6 @@ def degree(self, vertices=None, labels=False): else: return list(self.degree_iterator(vertices, labels)) - def vertices_with_degree(self, degree): - r""" - Return a list of vertices with the indicated degree. - - INPUT: - - - ``degree`` -- a degree to match with each vertex in the graph - - EXAMPLES: - - sage: G = graphs.CompleteGraph(5) - sage: G.vertices_with_degree(4) - [0, 1, 2, 3, 4] - sage: P = graphs.PathGraph(5) - sage: P.vertices_with_degree(1) - [0, 4] - """ - vertices = [] - for v, d in zip(self, self.degree_iterator()): - if d == degree: - vertices.append(v) - - return vertices - def average_degree(self): r""" Return the average degree of the graph. From aa9425fd66266db3693bce1adb605c377e8e8149 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sun, 16 Aug 2020 18:32:16 -0400 Subject: [PATCH 212/379] merged in examples and graph tickets. fixed errors in mobile linear extension cardinality computation. --- src/sage/combinat/posets/d_complete.py | 11 +-- src/sage/combinat/posets/hasse_diagram.py | 2 +- src/sage/combinat/posets/linear_extensions.py | 26 +++--- src/sage/combinat/posets/mobile.py | 82 ++++++------------- src/sage/combinat/posets/poset_examples.py | 5 +- src/sage/combinat/posets/posets.py | 2 +- src/sage/graphs/generic_graph.py | 24 ------ 7 files changed, 47 insertions(+), 105 deletions(-) diff --git a/src/sage/combinat/posets/d_complete.py b/src/sage/combinat/posets/d_complete.py index 02ffb8b043f..dd1e6cfc3c6 100644 --- a/src/sage/combinat/posets/d_complete.py +++ b/src/sage/combinat/posets/d_complete.py @@ -20,6 +20,7 @@ from .linear_extensions import LinearExtensionsOfPosetWithHooks from .lattices import FiniteJoinSemilattice from collections import deque +from sage.rings.integer_ring import ZZ from sage.misc.misc_c import prod class DCompletePoset(FiniteJoinSemilattice): @@ -121,7 +122,7 @@ def _hooks(self): queue.append(c) enqueued.add(c) - poset_hooks = {self._vertex_to_element(key): value for (key, value) in hooks.items()} + poset_hooks = {self._vertex_to_element(key): ZZ(value) for (key, value) in hooks.items()} return poset_hooks def get_hook(self, elmt): @@ -158,7 +159,7 @@ def hook_product(self): r""" Return the hook product for the poset. """ - if self._poset.cardinality() == 0: - return 1 - - return prod(self._hooks.values()) + if self.cardinality() == 0: + return ZZ(1) + + return ZZ(prod(self._hooks.values())) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 7160f629f6c..eca8bd63445 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -1177,7 +1177,7 @@ def order_ideal_cardinality(self, elements): seen.add(v) q.extend(self.neighbors_in(v)) - return size + return ZZ(size) def principal_order_ideal(self, i): """ diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index fc105f6e56c..2af60d23467 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -38,7 +38,7 @@ from sage.structure.list_clone import ClonableArray from sage.misc.misc_c import prod from sage.functions.other import factorial - +from sage.matrix.constructor import matrix class LinearExtensionOfPoset(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): @@ -866,10 +866,9 @@ def cardinality(self): num_elmts = self._poset.cardinality() if num_elmts == 0: - return 1 + return ZZ(1) - hooks = self._poset.get_hooks() - hook_product = prod(hooks.values()) + hook_product = self._poset.hook_product() return factorial(num_elmts) // hook_product class LinearExtensionsOfForest(LinearExtensionsOfPoset): @@ -894,7 +893,7 @@ def cardinality(self): """ return sum(self.atkinson(self._elements[0])) -class LinearExtensionsOfMobile(LinearExtensionOfPoset): +class LinearExtensionsOfMobile(LinearExtensionsOfPoset): r""" Linear extensions for a mobile poset. """ @@ -919,7 +918,7 @@ def cardinality(self): import sage.combinat.posets.posets as fp # Find folds if self._poset._anchor: - anchor_index = self._poset._ribbon.indexOf(self._poset._anchor[0]) + anchor_index = self._poset._ribbon.index(self._poset._anchor[0]) else: anchor_index = len(self._poset._ribbon) @@ -936,10 +935,9 @@ def cardinality(self): # Get ordered connected components cr = self._poset.cover_relations() - foldless_cr = [r for c in cr if (not r in folds)] - - elmts = self._poset._elements + foldless_cr = [tuple(c) for c in cr if tuple(c) not in folds] + elmts = list(self._poset._elements) poset_components = DiGraph([elmts, foldless_cr]) ordered_poset_components = list(map(lambda l: poset_components.connected_component_containing_vertex(l), [fold[1] for fold in fold_up] + [fold[0] for fold in fold_down])) @@ -949,18 +947,16 @@ def cardinality(self): # Return determinant mat = [] - for i in range(len(folds)): + for i in range(len(folds)+1): mat_poset = dc.DCompletePoset(self._poset.subposet(ordered_poset_components[i])) row = [0] * (i-1 if i-1 > 0 else 0) + [1] * (1 if i >= 1 else 0) + row.append(1 / mat_poset.hook_product()) for j, f in enumerate(folds[i:]): + next_poset = self._poset.subposet(ordered_poset_components[j+i+1]) + mat_poset = dc.DCompletePoset(fp.FinitePoset.slant_sum(mat_poset, next_poset, f[1], f[0])) row.append(1 / mat_poset.hook_product()) - if j + i < len(folds) - 1: - next_poset = self._poset.subposet(ordered_poset_components[j+i+1]) - - mat_poset = dc.DCompletePoset(fp.FinitePoset.slant_sum(mat_poset, next_poset, f[1], f[0])) mat.append(row) - return matrix(QQ, mat).determinant() * factorial(self._poset.cardinality()) diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index e6eec15aa68..d2e45c7eaed 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -19,7 +19,7 @@ class MobilePoset(FinitePoset): formula for counting linear extensions. """ - #_lin_ext_type = LinearExtensionsOfForest + _lin_ext_type = LinearExtensionsOfMobile _desc = 'Finite mobile poset' @@ -136,7 +136,7 @@ def _compute_ribbon(self): if G.is_path(): # Check if there is a anchor by seeing if there is more than one acyclic path to the next max - ends = max_elmt_graph.vertices_with_degree(1) + ends = max_elmt_graph.vertices(degree=1) # Form ribbon ribbon = G.shortest_path(ends[0], ends[1]) for end_count, end in enumerate(ends): @@ -147,37 +147,34 @@ def _compute_ribbon(self): return G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1]) return ribbon + # First check path counts between ends and deg3 vertex + # Then check if more than one max elmt on way to degree 3 vertex. + # Arbitrarily choose between ones with just 1 - else: - # First check path counts between ends and deg3 vertex - # Then check if more than one max elmt on way to degree 3 vertex. - # Arbitrarily choose between ones with just 1 + ends = max_elmt_graph.vertices(degree=1) + deg3 = max_elmt_graph.vertices(degree=3)[0] - ends = max_elmt_graph.vertices_with_degree(1) - deg3 = max_elmt_graph.vertices_with_degree(3)[0] - - anchoredEnd = None - for end in ends: - if not (H_un.is_cut_vertex(end) or H_un.degree(end) == 1): - anchoredEnd = end - break + anchoredEnd = None + for end in ends: + if not (H_un.is_cut_vertex(end) or H_un.degree(end) == 1): + anchoredEnd = end + break - if not anchoredEnd is None: - path = H.shortest_path(deg3, anchoredEnd) - ends.remove(anchoredEnd) + if not anchoredEnd is None: + path = H.shortest_path(deg3, anchoredEnd) + ends.remove(anchoredEnd) - return G.shortest_path(ends[0], ends[1]) + return G.shortest_path(ends[0], ends[1]) - else: - possible_anchors = ends[:] - for end in ends: - path = G.shortest_path(end, deg3) - if not sum(map(lambda z: z in max_elmts, path)) == 1: - possible_anchors.remove(end) + possible_anchors = ends[:] + for end in ends: + path = G.shortest_path(end, deg3) + if not sum(map(lambda z: z in max_elmts, path)) == 1: + possible_anchors.remove(end) - anchoredEnd = possible_anchors[0] - ends.remove(anchoredEnd) - return G.shortest_path(ends[0], ends[1]) + anchoredEnd = possible_anchors[0] + ends.remove(anchoredEnd) + return G.shortest_path(ends[0], ends[1]) def get_ribbon(self): r""" @@ -189,33 +186,4 @@ def get_anchor(self): r""" Returns the anchor of the mobile poset """ - return self._anchor - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + return self._anchor \ No newline at end of file diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 85d1f28f5b7..97592f295da 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -87,6 +87,7 @@ from sage.combinat.permutation import Permutations, Permutation, to_standard from sage.combinat.posets.posets import Poset, FinitePoset, FinitePosets_n from sage.combinat.posets.d_complete import DCompletePoset +from sage.combinat.posets.mobile import MobilePoset from sage.combinat.posets.lattices import (LatticePoset, MeetSemilattice, JoinSemilattice, FiniteLatticePoset) from sage.categories.finite_posets import FinitePosets @@ -1777,7 +1778,7 @@ def RibbonPoset(n, descents): sage: R.cover_relations() [[3, 4], [3, 2], [2, 1], [0, 1]] """ - return Poset([list(range(n)), [(i+1, i) if i in descents else (i, i+1) for i in range(n-1) ]]) + return MobilePoset(DiGraph([list(range(n)), [(i+1, i) if i in descents else (i, i+1) for i in range(n-1) ]])) @staticmethod def Mobile(ribbon, hangers, anchor=None): @@ -1833,7 +1834,7 @@ def Mobile(ribbon, hangers, anchor=None): cover_relations.append(((r, i, cr[0]), (r, i, cr[1]))) cover_relations.append(((r,i,h.top()), r)) - return Poset([elements, cover_relations]) + return MobilePoset(DiGraph([elements, cover_relations])) ## RANDOM LATTICES diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 487a611a93d..d166193f715 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6465,7 +6465,7 @@ def order_ideal_cardinality(self, elements): sage: P.order_ideal_cardinality([7,10]) 10 """ - vertices = list(map(self._element_to_vertex, elements)) + vertices = [self._element_to_vertex(elmt) for elmt in elements] return self._hasse_diagram.order_ideal_cardinality(vertices) def order_ideal_plot(self, elements): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 28e620166d1..e70ffc3d53d 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12101,30 +12101,6 @@ def degree(self, vertices=None, labels=False): else: return list(self.degree_iterator(vertices, labels)) - def vertices_with_degree(self, degree): - r""" - Return a list of vertices with the indicated degree. - - INPUT: - - - ``degree`` -- a degree to match with each vertex in the graph - - EXAMPLES: - - sage: G = graphs.CompleteGraph(5) - sage: G.vertices_with_degree(4) - [0, 1, 2, 3, 4] - sage: P = graphs.PathGraph(5) - sage: P.vertices_with_degree(1) - [0, 4] - """ - vertices = [] - for v in self.vertex_iterator(): - if self.degree(v) == degree: - vertices.append(v) - - return vertices - def average_degree(self): r""" Return the average degree of the graph. From 4805622344c0217c3518fe6428797f5bcee24bc9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 Aug 2020 18:22:19 -0700 Subject: [PATCH 213/379] build/pkgs/giac/spkg-src: Patch with giac-1.5.0.87-gsl_lapack.patch; remove old giac autotools patches --- build/pkgs/giac/checksums.ini | 6 +- build/pkgs/giac/package-version.txt | 2 +- .../patches/autotools/configure-libpng.patch | 12 --- .../autotools/giac-1.5.0.87-gsl_lapack.patch | 83 +++++++++++++++++++ .../autotools/ticket-22315-autotools.patch | 48 ----------- build/pkgs/giac/spkg-src | 15 +++- 6 files changed, 99 insertions(+), 67 deletions(-) delete mode 100644 build/pkgs/giac/patches/autotools/configure-libpng.patch create mode 100644 build/pkgs/giac/patches/autotools/giac-1.5.0.87-gsl_lapack.patch delete mode 100644 build/pkgs/giac/patches/autotools/ticket-22315-autotools.patch diff --git a/build/pkgs/giac/checksums.ini b/build/pkgs/giac/checksums.ini index b313d50549c..71293fdadc7 100644 --- a/build/pkgs/giac/checksums.ini +++ b/build/pkgs/giac/checksums.ini @@ -1,5 +1,5 @@ tarball=giac-VERSION.tar.bz2 -sha1=c8b4b40dae98a83adda0142332a482c4fec2d4eb -md5=504151eb5f483a24c941822d792f0bb3 -cksum=3611162964 +sha1=873f44e8b6a8f43121c30587d99016763c9e047c +md5=52eda3193e16ab05b4fb2a727c8f3308 +cksum=2879733923 upstream_url=https://trac.sagemath.org/raw-attachment/ticket/29552/giac-VERSION.tar.bz2 diff --git a/build/pkgs/giac/package-version.txt b/build/pkgs/giac/package-version.txt index 913adb80abb..d24489e06fe 100644 --- a/build/pkgs/giac/package-version.txt +++ b/build/pkgs/giac/package-version.txt @@ -1 +1 @@ -1.5.0.87 +1.5.0.87p0 diff --git a/build/pkgs/giac/patches/autotools/configure-libpng.patch b/build/pkgs/giac/patches/autotools/configure-libpng.patch deleted file mode 100644 index deabc0eb8ab..00000000000 --- a/build/pkgs/giac/patches/autotools/configure-libpng.patch +++ /dev/null @@ -1,12 +0,0 @@ -patch configure.in to correctly check multiple libpng soname versions -the resulting patch to configure is in ../configure-libpng.patch -see https://trac.sagemath.org/ticket/25818 ---- a/configure.in 2018-07-10 18:26:56.330278000 +0000 -+++ b/configure.in 2018-07-10 19:06:58.602278000 +0000 -@@ -100,7 +100,7 @@ - [ if test "x$enableval" = "xno"; then CONFIG_PNG="no"; fi], []) - - if test "x$CONFIG_PNG" = "xyes"; then -- AC_CHECK_HEADERS(png.h, AC_CHECK_LIB(png,main)) dnl AC_CHECK_LIBS(main,[png16,png14,png12,png])) -+ AC_CHECK_HEADERS(png.h, AC_SEARCH_LIBS(png_sig_cmp,[png16 png14 png12 png])) - fi diff --git a/build/pkgs/giac/patches/autotools/giac-1.5.0.87-gsl_lapack.patch b/build/pkgs/giac/patches/autotools/giac-1.5.0.87-gsl_lapack.patch new file mode 100644 index 00000000000..1ecb49fa081 --- /dev/null +++ b/build/pkgs/giac/patches/autotools/giac-1.5.0.87-gsl_lapack.patch @@ -0,0 +1,83 @@ +diff --git a/configure.in b/configure.in +index 204d17a..434e4c9 100644 +--- a/configure.in ++++ b/configure.in +@@ -38,6 +38,7 @@ AC_LANG([C++]) + AC_PROG_LIBTOOL + AC_PROG_YACC + AM_PROG_LEX ++PKG_PROG_PKG_CONFIG + AC_C_BIGENDIAN + + dnl Check for standard C+headers +@@ -139,47 +140,17 @@ AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(long long) + + dnl Checking for Gnu Sci Lib +-CONFIG_GSL="yes" +-AC_ARG_ENABLE([gsl],[ +- AS_HELP_STRING([--enable-gsl], [Use GNU scientific library [[default=yes]]])], +- [ if test "x$enableval" = "xno"; then CONFIG_GSL="no"; fi], []) +- +-if test "$CONFIG_GSL" = "yes"; then +- AC_CHECK_HEADERS(gsl/gsl_blas.h) +- AC_CHECK_HEADERS(gsl/gsl_eigen.h) +- if test "$ac_cv_header_gsl_gsl_blas_h" != "yes" -o "$ac_cv_header_gsl_gsl_eigen_h" != "yes"; then +- CONFIG_GSL="no" +- fi +-fi +-if test "$CONFIG_GSL" = "yes"; then +- save_LIBS="$LIBS" +- AC_CHECK_LIB(gslcblas, main, [], [CONFIG_GSL="no"]) +- AC_CHECK_LIB(gsl, gsl_sf_gamma, [], [CONFIG_GSL="no"]) +- LIBS="$save_LIBS" +- fi +-GSL_LIBS="" +-if test "$CONFIG_GSL" = "yes"; then +- GSL_LIBS="-lgsl -lgslcblas" +- fi +-AC_SUBST(CONFIG_GSL) +-AC_SUBST(GSL_LIBS) +-AM_CONDITIONAL(CONFIG_GSL, [test "$CONFIG_GSL" = "yes"]) +- +-CONFIG_LAPACK="yes" +-AC_ARG_ENABLE([lapack], +- [AS_HELP_STRING([--enable-lapack], [Use LAPACK [[default=yes]]])], +- [ if test "$enableval" = "no"; then CONFIG_LAPACK="no"; fi], []) ++PKG_CHECK_MODULES([GSL], [gsl],[ ++ AC_DEFINE(HAVE_LIBGSL,1, [Define if gsl is installed]) ++ AC_SUBST(GSL_LIBS)]) + +-if test "$CONFIG_LAPACK" = "yes"; then +-# AC_CHECK_LIB(f2c, main, [], [CONFIG_LAPACK="no"]) +-# AC_CHECK_LIB(blas, main, [], [CONFIG_LAPACK="no"]) +-# AC_CHECK_LIB(tmglib, main, [], [CONFIG_LAPACK="no"]) +- AC_CHECK_LIB(gfortran, main) +- AC_CHECK_LIB(blas, main) +- AC_CHECK_LIB(lapack, main, [], [CONFIG_LAPACK="no"]) +-# AX_BLAS([have_blas=yes],[have_blas=no]) +-# AX_LAPACK([have_lapack=yes],[have_lapack=no]) +-fi ++PKG_CHECK_MODULES([LAPACK], [lapack],[ ++ AC_DEFINE(HAVE_LIBLAPACK,1,[Define if LAPACK is installed]) ++ AC_SUBST(LAPACK_LIBS)]) ++ ++PKG_CHECK_MODULES([BLAS], [blas],[ ++ AC_DEFINE(HAVE_BLAS,1,[Define if BLAS is installed]) ++ AC_SUBST(BLAS_LIBS)]) + + CONFIG_PARI="yes" + AC_ARG_ENABLE([pari], +diff --git a/src/Makefile.am b/src/Makefile.am +index b5dd325..d45b553 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -14,7 +14,7 @@ libgiac_la_SOURCES = input_lexer.ll sym2poly.cc gausspol.cc threaded.cc \ + help.cc lpsolve.cc optimization.cc signalprocessing.cc \ + graphe.cc graphtheory.cc nautywrapper.c markup.cc kdisplay.cc kadd.cc # Ugh.. + +-libgiac_la_LIBADD = $(NTL_LIBS) $(COCOA_LIBS) $(PARI_LIBS) $(GSL_LIBS) ++libgiac_la_LIBADD = $(NTL_LIBS) $(COCOA_LIBS) $(PARI_LIBS) $(GSL_LIBS) $(LAPACK_LIBS) $(BLAS_LIBS) + + giacincludedir = $(includedir)/giac + giacinclude_HEADERS = dispatch.h fraction.h gen.h desolve.h misc.h ti89.h \ diff --git a/build/pkgs/giac/patches/autotools/ticket-22315-autotools.patch b/build/pkgs/giac/patches/autotools/ticket-22315-autotools.patch deleted file mode 100644 index b660982360b..00000000000 --- a/build/pkgs/giac/patches/autotools/ticket-22315-autotools.patch +++ /dev/null @@ -1,48 +0,0 @@ -diff -ruN a/Makefile.am b/Makefile.am ---- a/Makefile.am 2017-02-06 16:01:16.484367300 +0100 -+++ b/Makefile.am 2017-02-06 16:06:28.013569300 +0100 -@@ -8,18 +8,18 @@ - ## FIXME: installing autoconf-generated config.h is just PLAIN WRONG. - - install-exec-hook: -- cp config.h $(DESTDIR)/$(includedir)/giac -- ./mkinstalldirs $(DESTDIR)/$(prefix)/share/ -- ./mkinstalldirs $(DESTDIR)/$(prefix)/share/pixmaps -- cp xcas.xpm $(DESTDIR)/$(prefix)/share/pixmaps/xcas.xpm -- ./mkinstalldirs $(DESTDIR)/$(prefix)/share/applications -- cp xcas.desktop $(DESTDIR)/$(prefix)/share/applications/xcas.desktop -- ./mkinstalldirs $(DESTDIR)/$(prefix)/share/application-registry -- cp xcas.applications $(DESTDIR)/$(prefix)/share/application-registry/xcas.applications -- ./mkinstalldirs $(DESTDIR)/$(prefix)/share/icons/ -- ./mkinstalldirs $(DESTDIR)/$(prefix)/share/icons/hicolor -- for SIZE in 256 128 64 32 16; do ./mkinstalldirs $(DESTDIR)/$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE} && ./mkinstalldirs $(DESTDIR)/$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/apps && cp icons/xcas_$${SIZE}.png $(DESTDIR)/$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/apps/xcas.png; done -- for SIZE in 256 128 64 32 16; do ./mkinstalldirs $(DESTDIR)/$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/mimetypes && cp icons/x-xcas_$${SIZE}.png $(DESTDIR)/$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/mimetypes/application-x-xcas.png; done -+ cp config.h $(DESTDIR)$(includedir)/giac -+ ./mkinstalldirs $(DESTDIR)$(prefix)/share/ -+ ./mkinstalldirs $(DESTDIR)$(prefix)/share/pixmaps -+ cp xcas.xpm $(DESTDIR)$(prefix)/share/pixmaps/xcas.xpm -+ ./mkinstalldirs $(DESTDIR)$(prefix)/share/applications -+ cp xcas.desktop $(DESTDIR)$(prefix)/share/applications/xcas.desktop -+ ./mkinstalldirs $(DESTDIR)$(prefix)/share/application-registry -+ cp xcas.applications $(DESTDIR)$(prefix)/share/application-registry/xcas.applications -+ ./mkinstalldirs $(DESTDIR)$(prefix)/share/icons/ -+ ./mkinstalldirs $(DESTDIR)$(prefix)/share/icons/hicolor -+ for SIZE in 256 128 64 32 16; do ./mkinstalldirs $(DESTDIR)$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE} && ./mkinstalldirs $(DESTDIR)$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/apps && cp icons/xcas_$${SIZE}.png $(DESTDIR)$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/apps/xcas.png; done -+ for SIZE in 256 128 64 32 16; do ./mkinstalldirs $(DESTDIR)$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/mimetypes && cp icons/x-xcas_$${SIZE}.png $(DESTDIR)$(prefix)/share/icons/hicolor/$${SIZE}x$${SIZE}/mimetypes/application-x-xcas.png; done - - ACLOCAL_AMFLAGS = -I m4 - -diff -ruN a/src/Makefile.am b/src/Makefile.am ---- a/src/Makefile.am 2017-02-06 16:01:51.663379800 +0100 -+++ b/src/Makefile.am 2017-02-06 16:01:25.730657700 +0100 -@@ -85,8 +85,8 @@ - bin_SCRIPTS = pgiac - - install-exec-hook: -- rm -f $(DESTDIR)$(bindir)/cas_help -- mv $(DESTDIR)/$(bindir)/aide$(EXEEXT) $(DESTDIR)$(bindir)/cas_help -+ rm -f $(DESTDIR)$(bindir)/cas_help$(EXEEXT) -+ mv $(DESTDIR)$(bindir)/aide$(EXEEXT) $(DESTDIR)$(bindir)/cas_help - ln -sf cas_help $(DESTDIR)$(bindir)/en_cas_help - ln -sf cas_help $(DESTDIR)$(bindir)/es_cas_help - ln -sf cas_help $(DESTDIR)$(bindir)/fr_cas_help diff --git a/build/pkgs/giac/spkg-src b/build/pkgs/giac/spkg-src index 910da816bc4..ce9ebcb7d91 100755 --- a/build/pkgs/giac/spkg-src +++ b/build/pkgs/giac/spkg-src @@ -15,7 +15,7 @@ set -e VERSION="1.5.0" VERSIONREV="87" -PATCHSUFFIX="" +PATCHSUFFIX="p0" # The upstream tarball name is: giac"$SOURCEORIG".tar.gz SOURCEORIG=_"$VERSION"-"$VERSIONREV" @@ -67,9 +67,15 @@ touch html_mtt touch html_vall # - # building giac source tarball for the spkg -cd ../../../ +cd ../../ + +for a in "$SAGE_ROOT"/build/pkgs/giac/patches/autotools/*.patch; do + patch -p1 < $a +done +autoreconf -fi + +cd .. tar -cjf "$OUTPUTFILEBASENAME".tar.bz2 src # cleaning extracted dir. @@ -78,3 +84,6 @@ rm -rf "$TARGET" # going back to starting dir cd "$ORIGDIR" +#$SAGE_ROOT/sage -package update giac "$VERSION"."$VERSIONREV""$PATCHSUFFIX" +echo "$VERSION"."$VERSIONREV""$PATCHSUFFIX" > build/pkgs/giac/package-version.txt +$SAGE_ROOT/sage -package fix-checksum giac From 1cec2e794f3d03000a50c94ce9f4b69071757284 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 Aug 2020 18:33:54 -0700 Subject: [PATCH 214/379] build/pkgs/giac/spkg-install.in: Remove --disable-lapack --- build/pkgs/giac/spkg-install.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in index d6a931b3b10..b4b2c4905cb 100644 --- a/build/pkgs/giac/spkg-install.in +++ b/build/pkgs/giac/spkg-install.in @@ -40,7 +40,6 @@ echo "Configuring giac..." # --disable-ao (avoid libao deps) -# --disable-lapack (avoid lapack and blas deps because they could be from the base system) # On OS X (10.12) the built in intl is broken DISABLENLS="" if [ "$UNAME" = "Darwin" ]; then @@ -48,7 +47,7 @@ if [ "$UNAME" = "Darwin" ]; then DISABLENLS="--disable-nls" fi -sdh_configure --disable-gui --disable-ao --disable-lapack "$DISABLENLS" --enable-png=no --disable-samplerate +sdh_configure --disable-gui --disable-ao "$DISABLENLS" --enable-png=no --disable-samplerate ############################################################# # Build From eb7995f997cb53baa572bfd8543dfb2b887bcf2e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 Aug 2020 18:34:06 -0700 Subject: [PATCH 215/379] build/pkgs/giac/dependencies: Add curl, glpk --- build/pkgs/giac/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/giac/dependencies b/build/pkgs/giac/dependencies index 72a653466b0..c36972a1011 100644 --- a/build/pkgs/giac/dependencies +++ b/build/pkgs/giac/dependencies @@ -1,4 +1,4 @@ -readline libpng $(MP_LIBRARY) mpfr mpfi ntl gsl pari +readline libpng $(MP_LIBRARY) mpfr mpfi ntl gsl pari glpk curl ---------- All lines of this file are ignored except the first. From d8955e02d1249ba753bcfb0d052dd7915e885ab9 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 17 Aug 2020 11:39:35 +0200 Subject: [PATCH 216/379] trac #30377: fix block syntax in fast_digraph.pyx --- .../graph_decompositions/fast_digraph.pyx | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/fast_digraph.pyx b/src/sage/graphs/graph_decompositions/fast_digraph.pyx index 33ee68daf6a..63e19e2e811 100644 --- a/src/sage/graphs/graph_decompositions/fast_digraph.pyx +++ b/src/sage/graphs/graph_decompositions/fast_digraph.pyx @@ -9,8 +9,8 @@ cardinality). **Sets and integers :** -In the following code, sets are represented as integers, where the ith bit is -set if element i belongs to the set. +In the following code, sets are represented as integers, where the `i`-th bit is +set if element `i` belongs to the set. """ from libc.stdint cimport uint8_t @@ -33,7 +33,7 @@ cdef class FastDigraph: between `[0..n-1]` and the set of vertices of the input (Di)Graph, ``list(D)`` by default - TESTS:: + EXAMPLES:: sage: cython_code = [ ....: 'from sage.graphs.graph import Graph', @@ -46,7 +46,9 @@ cdef class FastDigraph: [1, 2, 1] """ if D.order() > 8*sizeof(int): - raise OverflowError("Too many vertices. This structure can only encode digraphs on at most %i vertices"%(8*sizeof(int))) + raise OverflowError("Too many vertices. This structure can only " + "encode digraphs on at most " + "%i vertices"%(8 * sizeof(int))) self.n = D.order() self.graph = check_calloc(self.n, sizeof(int)) @@ -91,7 +93,7 @@ cdef class FastDigraph: r""" Displays the adjacency matrix of ``self``. - TESTS:: + EXAMPLES:: sage: cython_code = [ ....: 'from sage.graphs.graph import Graph', @@ -105,19 +107,20 @@ cdef class FastDigraph: cdef int i, j for i in range(self.n): for j in range(self.n): - print(((self.graph[i]>>j)&1), end="") + print(((self.graph[i]>>j) & 1), end="") print("") cdef inline int compute_out_neighborhood_cardinality(FastDigraph g, int S): r""" - Returns the cardinality of `N^+(S)\S`. + Return the cardinality of `N^+(S)\S`. INPUT: - ``g`` -- a FastDigraph + - ``S`` -- an integer describing the set - TESTS:: + EXAMPLES:: sage: cython_code = [ ....: 'from sage.graphs.graph import Graph', @@ -132,19 +135,19 @@ cdef inline int compute_out_neighborhood_cardinality(FastDigraph g, int S): cdef int i cdef int tmp = 0 for i in range(g.n): - tmp |= g.graph[i] & (-((S >> i)&1)) + tmp |= g.graph[i] & (-((S >> i) & 1)) tmp &= (~S) return popcount32(tmp) cdef inline int popcount32(int i): - """ + r""" Return the number of '1' bits in a 32-bits integer. - If sizeof(int) > 4, this function only returns the number of '1' - bits in (i & ((1<<32) - 1)). + If ``sizeof(int) > 4``, this function only returns the number of '1' + bits in ``(i & ((1<<32) - 1))``. - TESTS:: + EXAMPLES:: sage: cython_code = [ ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport popcount32', @@ -153,9 +156,9 @@ cdef inline int popcount32(int i): sage: cython(os.linesep.join(cython_code)) [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4] """ - i = i - ((i >> 1) & 0x55555555); - i = (i & 0x33333333) + ((i >> 2) & 0x33333333); - return ((i + (i >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24; + i = i - ((i >> 1) & 0x55555555) + i = (i & 0x33333333) + ((i >> 2) & 0x33333333) + return ((i + (i >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24 # If you happened to be doubting the consistency of the popcount32 function @@ -174,7 +177,7 @@ def test_popcount(): """ cdef int i = 1 # While the last 32 bits of i are not equal to 0 - while (i & ((1<<32) - 1)) : + while (i & ((1 << 32) - 1)): if popcount32(i) != slow_popcount32(i): print("Error for i = ", str(i)) print("Result with popcount32 : " + str(popcount32(i))) @@ -186,10 +189,10 @@ cdef inline int slow_popcount32(int i): """ Return the number of '1' bits in a 32-bits integer. - If sizeof(int) > 4, this function only returns the number of '1' - bits in (i & ((1<<32) - 1)). + If ``sizeof(int) > 4``, this function only returns the number of '1' + bits in ``(i & ((1<<32) - 1))``. - TESTS:: + EXAMPLES:: sage: cython_code = [ ....: 'from sage.graphs.graph_decompositions.fast_digraph cimport popcount32', @@ -204,6 +207,6 @@ cdef inline int slow_popcount32(int i): cdef int k for k in range(32): - j += (i>>k) & 1 + j += (i >> k) & 1 return j From 15a3b097e90e8823743457b6c624792bae927153 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 17 Aug 2020 10:37:58 -0400 Subject: [PATCH 217/379] 29844: fixed hash example --- src/sage/schemes/berkovich/berkovich_space.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index ab648148310..c9728af3476 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -259,9 +259,10 @@ def __hash__(self): sage: R. = QQ[] sage: A. = NumberField(z^2 + 1) - sage: B = Berkovich_Cp_Projective(A, A.prime_above(5)) - sage: hash(B) - 1629824360745675264 + sage: B = Berkovich_Cp_Projective(A, A.primes_above(5)[0]) + sage: C = Berkovich_Cp_Projective(A, A.primes_above(5)[1]) + sage: hash(B) != hash(C) + True """ if self._base_type == 'padic field': return hash(self.prime()) @@ -395,7 +396,7 @@ class Berkovich_Cp_Affine(Berkovich_Cp): def __init__(self, base, ideal=None): if base in ZZ: if base.is_prime(): - base = Qp(base) #TODO chance to Qpbar + base = Qp(base) # change to Qpbar else: raise ValueError("non-prime pased into Berkovich space") if is_AffineSpace(base): @@ -417,7 +418,7 @@ def __init__(self, base, ideal=None): if not ideal.is_prime(): raise ValueError('passed non prime ideal') self._base_type = 'number field' - elif is_pAdicField(base): #TODO change base to Qpbar(prime) + elif is_pAdicField(base): # change base to Qpbar prime = base.prime() ideal = None self._base_type = 'padic field' From 6a6eac35ad708a4a077616544b0794ff8acf5eb9 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 17 Aug 2020 13:58:59 -0400 Subject: [PATCH 218/379] Fixed a bug in the mobile example with the anchor. Added documentation for mobile. Fixed a sorting bug in the cardinality method --- src/doc/en/reference/references/index.rst | 3 ++ src/sage/combinat/posets/linear_extensions.py | 8 ++-- src/sage/combinat/posets/mobile.py | 38 +++++++++++++++++-- src/sage/combinat/posets/poset_examples.py | 4 +- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index b5fd40b2265..95b646fc4b0 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2340,6 +2340,9 @@ REFERENCES: Riemann surfaces and Dessins d'enfant*, (2011) London Mathematical Society, Student Text 79. +.. [GGMM2020] A. Garver, S. Grosser, J. Matherne, A. Morales, Counting linear extensions of + posets with determinants of hook lengths, :arxiv:`2001.08822` + .. [GGNS2013] \B. Gerard, V. Grosso, M. Naya-Plasencia, and F.-X. Standaert, *Block ciphers that are easier to mask: How far can we go?*; in CHES, (2013), pp. 383-399. diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 2af60d23467..e874b513cef 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -866,7 +866,7 @@ def cardinality(self): num_elmts = self._poset.cardinality() if num_elmts == 0: - return ZZ(1) + return 1 hook_product = self._poset.hook_product() return factorial(num_elmts) // hook_product @@ -939,11 +939,11 @@ def cardinality(self): elmts = list(self._poset._elements) poset_components = DiGraph([elmts, foldless_cr]) - ordered_poset_components = list(map(lambda l: poset_components.connected_component_containing_vertex(l), - [fold[1] for fold in fold_up] + [fold[0] for fold in fold_down])) + ordered_poset_components = [poset_components.connected_component_containing_vertex(l, sort=False) + for l in [fold[1] for fold in fold_up] + [fold[0] for fold in fold_down]] ordered_poset_components.append(poset_components.connected_component_containing_vertex( folds[-1][1] if len(fold_down) > 0 else folds[-1][0] - )) + , sort=False)) # Return determinant mat = [] diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index d2e45c7eaed..60a9a6abca1 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -15,8 +15,13 @@ class MobilePoset(FinitePoset): r""" + A mobile poset. + Mobile posets are an extension of d-complete posets which permit a determinant - formula for counting linear extensions. + formula for counting linear extensions. They are formed by having a ribbon + poset with d-complete posets 'hanging' below it and at most one + d-complete poset above it, known as the anchor. See [GGMM2020]_ + for the definition. """ _lin_ext_type = LinearExtensionsOfMobile @@ -24,6 +29,35 @@ class MobilePoset(FinitePoset): def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None): + r""" + EXAMPLES:: + + sage: P = posets.Mobile(posets.RibbonPoset(7, [1,3]), {1: + ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, + ....: anchor=(4, 2, posets.ChainPoset(6))) + sage: type(P) + + + The internal data structure consists of: + + - the data required to form a finite poset (see + :class:`sage.combinat.posets.posets.FinitePoset`):: + + - ``ribbon`` -- an ordered list of elements that are a path in the + undirected Hasse diagram and form the ribbon of the mobile + + TESTS:: + + sage: P._ribbon + [(4, 5), (4, 4), (4, 3), (4, 2), 4, 3, 2, 1] + sage: P._anchor + (4, 5) + sage: P.linear_extensions().cardinality() + 361628701868606400 + + + See also the tests in the class documentation. + """ FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, category=category, facade=facade, key=key) if ribbon and self._test_valid_ribbon(ribbon): self._ribbon = ribbon @@ -117,7 +151,6 @@ def _compute_ribbon(self): H = self._hasse_diagram H_un = H.to_undirected() max_elmts = H.sinks() - # Compute anchor, ribbon ribbon = [] # In order list of elements on zigzag @@ -130,7 +163,6 @@ def _compute_ribbon(self): for m in max_elmts[1:]: sp = H_un.shortest_path(start, m) zigzag_elmts.update(sp) - max_elmt_graph = H.subgraph(zigzag_elmts) G = max_elmt_graph.to_undirected() diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 97592f295da..a176e7bc1f9 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -1821,10 +1821,10 @@ def Mobile(ribbon, hangers, anchor=None): if anchor: for cr in anchor[2].cover_relations(): cover_relations.append(((anchor[0], cr[0]), (anchor[0], cr[1]))) - cover_relations.append((anchor[0], anchor[1])) + cover_relations.append((anchor[0], (anchor[0], anchor[1]))) for elmt in anchor[2]._elements: - elements.extend((anchor[0], elmt)) + elements.append((anchor[0], elmt)) for r, hangs in hangers.items(): for i, h in enumerate(hangs): From c83f1bed40cd9425e66396b56b1e83e39bda17b4 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 17 Aug 2020 14:14:05 -0400 Subject: [PATCH 219/379] changed from ``degree`` to ``property`` and patch ``is_path`` documentation --- src/sage/graphs/generic_graph.py | 24 ++++++++++++++---------- src/sage/graphs/graph.py | 8 ++++++-- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index e70ffc3d53d..93334470f87 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10417,7 +10417,7 @@ def get_vertices(self, verts=None): return {v: self._assoc.get(v, None) for v in verts} - def vertex_iterator(self, vertices=None, degree=None): + def vertex_iterator(self, vertices=None, property=None): """ Return an iterator over the given vertices. @@ -10430,8 +10430,9 @@ def vertex_iterator(self, vertices=None, degree=None): - ``vertices`` -- iterated vertices are these intersected with the vertices of the (di)graph - - ``degree`` -- a nonnegative integer (default: ``None``) that specifies - vertices with the given degree + - ``property`` -- function (default: ``None``); a function that inputs + a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if + ``property(v) == True`` EXAMPLES:: @@ -10472,8 +10473,10 @@ def vertex_iterator(self, vertices=None, degree=None): sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop """ - if degree: - return [v for v, d in self.degree_iterator(labels=True) if d == degree] + if property is not None: + for v in self._backend.iterator_verts(vertices): + if property(v): + yield v return self._backend.iterator_verts(vertices) @@ -10551,7 +10554,7 @@ def neighbor_iterator(self, vertex, closed=False): for u in self._backend.iterator_nbrs(vertex): yield u - def vertices(self, sort=True, key=None, degree=None): + def vertices(self, sort=True, key=None, property=None): r""" Return a list of the vertices. @@ -10564,8 +10567,9 @@ def vertices(self, sort=True, key=None, degree=None): vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have ``sort=True``) - - ``degree`` -- a nonnegative integer (default: ``None``); an integer - that specifies vertices only with the given degree + - ``property`` -- function (default: ``None``); a function that inputs + a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if + ``property(v) == True`` OUTPUT: @@ -10644,8 +10648,8 @@ def vertices(self, sort=True, key=None, degree=None): if (not sort) and key: raise ValueError('sort keyword is False, yet a key function is given') if sort: - return sorted(self.vertex_iterator(degree=degree), key=key) - return list(self.vertex_iterator(degree=degree)) + return sorted(self.vertex_iterator(property=property), key=key) + return list(self.vertex_iterator(property=property)) def neighbors(self, vertex, closed=False): """ diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index d3bc6f09a92..6438c0d3618 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3234,7 +3234,11 @@ def is_semi_symmetric(self): @doc_index("Graph properties") def is_path(self): r""" - Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). + Check whether ``self`` is a path. + + A connected graph of order `n \geq 2` is a path if it is a tree (see :meth:`is_tree`) + with `n-2` vertices of degree 2 and two of degree 1. + By convention, a graph of order 1 without loops is a path, but the empty graph is not a path. EXAMPLES: @@ -3263,7 +3267,7 @@ def is_path(self): return False if order <= 1: - return True if order else False + return order == 1 deg_one_counter = 0 seen_counter = 0 From dd9cc18b3ae3406de672657b4ffaa3e2728f1dd9 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 17 Aug 2020 14:29:05 -0400 Subject: [PATCH 220/379] changed name to vertex_property --- src/sage/graphs/generic_graph.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 93334470f87..5ca6aa29b06 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10417,7 +10417,7 @@ def get_vertices(self, verts=None): return {v: self._assoc.get(v, None) for v in verts} - def vertex_iterator(self, vertices=None, property=None): + def vertex_iterator(self, vertices=None, vertex_property=None): """ Return an iterator over the given vertices. @@ -10430,9 +10430,9 @@ def vertex_iterator(self, vertices=None, property=None): - ``vertices`` -- iterated vertices are these intersected with the vertices of the (di)graph - - ``property`` -- function (default: ``None``); a function that inputs + - ``vertex_property`` -- function (default: ``None``); a function that inputs a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if - ``property(v) == True`` + ``vertex_property(v) == True`` EXAMPLES:: @@ -10473,9 +10473,9 @@ def vertex_iterator(self, vertices=None, property=None): sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop """ - if property is not None: + if vertex_property is not None: for v in self._backend.iterator_verts(vertices): - if property(v): + if vertex_property(v): yield v return self._backend.iterator_verts(vertices) @@ -10554,7 +10554,7 @@ def neighbor_iterator(self, vertex, closed=False): for u in self._backend.iterator_nbrs(vertex): yield u - def vertices(self, sort=True, key=None, property=None): + def vertices(self, sort=True, key=None, vertex_property=None): r""" Return a list of the vertices. @@ -10567,9 +10567,9 @@ def vertices(self, sort=True, key=None, property=None): vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have ``sort=True``) - - ``property`` -- function (default: ``None``); a function that inputs + - ``vertex_property`` -- function (default: ``None``); a function that inputs a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if - ``property(v) == True`` + ``vertex_property(v) == True`` OUTPUT: @@ -10648,8 +10648,8 @@ def vertices(self, sort=True, key=None, property=None): if (not sort) and key: raise ValueError('sort keyword is False, yet a key function is given') if sort: - return sorted(self.vertex_iterator(property=property), key=key) - return list(self.vertex_iterator(property=property)) + return sorted(self.vertex_iterator(vertex_property=vertex_property), key=key) + return list(self.vertex_iterator(vertex_property=vertex_property)) def neighbors(self, vertex, closed=False): """ From 228e2feba37674be07b847c87e7d8bf7b7907395 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 17 Aug 2020 14:37:59 -0400 Subject: [PATCH 221/379] 29844: removed unused variables and added examples --- .../schemes/berkovich/berkovich_cp_element.py | 28 ++++++++----------- src/sage/schemes/berkovich/berkovich_space.py | 14 ++++++++++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index b5ad04be3eb..ef9e28a73ae 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -39,12 +39,8 @@ from sage.rings.padics.padic_generic_element import pAdicGenericElement from sage.rings.padics.padic_base_generic import pAdicBaseGeneric from sage.rings.padics.generic_nodes import is_pAdicField -from sage.rings.padics.factory import Qp -from sage.schemes.projective.projective_space import is_ProjectiveSpace, ProjectiveSpace +from sage.schemes.projective.projective_space import ProjectiveSpace from sage.schemes.projective.projective_point import SchemeMorphism_point_projective_field -from sage.schemes.generic.morphism import is_SchemeMorphism -from sage.schemes.affine.affine_space import AffineSpace -from sage.schemes.generic.scheme import Scheme from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.rings.infinity import Infinity @@ -185,14 +181,14 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise TypeError('could not convert %s to %s' %(center, self._base_space)) if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError("could not convert %s to %s" %(center, self._base_space)) else: # center is padic, not but an element of a scheme over a padic field. @@ -205,7 +201,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if center not in self._base_space: try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError('could not convert %s to %s' %(center, self._base_space)) if center.scheme().ambient_space() != center.scheme(): raise ValueError("the center of a point of Berkovich space over " + \ @@ -244,7 +240,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise TypeError("could not convert %s to %s" %(center, self._base_space)) elif not is_pAdicField(center.parent()): #center is padic, not but an element of a padic field. we convert to padic field @@ -256,7 +252,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if center.parent() == self._base_space: try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError('could not convert %s to %s' %(center, self._base_space)) #make sure the radius coerces into the reals if not is_RealNumber(radius): @@ -289,14 +285,14 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) - except (ValueError, TypeError) as e: + except (ValueError, TypeError): raise TypeError("could not convert %s to %s" %(center, self._base_space)) if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError("could not convert %s to %s" %(center, self._base_space)) else: # center is padic, not but an element of a scheme over a padic field. @@ -304,7 +300,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= field_scheme = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1) try: center = field_scheme(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError('could not convert %s to %s' %center, field_scheme) if center.scheme().base_ring().prime() != self._p: raise ValueError("center must be an element of " + \ @@ -313,7 +309,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if center not in self._base_space: try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError('could not convert %s to %s' %(center, self._base_space)) if not(center.scheme().ambient_space() is center.scheme()): raise ValueError("the center of a point of projective Berkovich space cannot be " + \ @@ -326,7 +322,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise TypeError("could not convert %s to %s" %(center, self._base_space)) elif not is_pAdicField(center.parent()): #center is padic, not but an element of a padic field. we convert to padic field @@ -338,7 +334,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if not(center.parent() == self._base_space): try: center = (self._base_space)(center) - except (TypeError, ValueError) as e: + except (TypeError, ValueError): raise ValueError('could not convert %s to %s' %(center, self._base_space)) else: raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index c9728af3476..a4210eff43f 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -53,6 +53,13 @@ def is_Berkovich(space): - ``True`` if ``space`` is a Berkovich space. - ``False`` otherwise. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: from sage.schemes.berkovich.berkovich_space import is_Berkovich + sage: is_Berkovich(B) + True """ return isinstance(space, Berkovich) @@ -64,6 +71,13 @@ def is_Berkovich_Cp(space): - ``True`` if ``space`` is a Berkovich space over ``Cp``. - ``False`` otherwise. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(3) + sage: from sage.schemes.berkovich.berkovich_space import is_Berkovich + sage: is_Berkovich(B) + True """ return isinstance(space, Berkovich_Cp) From e7b86f75938c3cb2c999041d4b0ba92fc12ade62 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 17 Aug 2020 16:02:08 -0400 Subject: [PATCH 222/379] 29844: added examples --- .../schemes/berkovich/berkovich_cp_element.py | 36 +++++++++---------- src/sage/schemes/berkovich/berkovich_space.py | 4 +-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index ef9e28a73ae..1a1a14afa32 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -409,7 +409,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= def _custom_abs(self, x): """ - Returns the absolute value of ``x`` with respect to the norm on ``Cp``. + Return the absolute value of ``x`` with respect to the norm on ``Cp``. Used to simplify code, as ``x`` may be a point of a number field or a p-adic field. @@ -438,7 +438,7 @@ def _custom_abs(self, x): def center_function(self): """ - Returns the function defining the centers of disks in the approximation. + Return the function defining the centers of disks in the approximation. Not defined unless this point is a type IV point created by using a univariate function to compute centers. @@ -466,7 +466,7 @@ def center_function(self): def radius_function(self): """ - Returns the function defining the radii of disks in the approximation. + Return the function defining the radii of disks in the approximation. Not defined unless this point is a type IV point created by using a univariate function to compute radii. @@ -494,7 +494,7 @@ def radius_function(self): def precision(self): """ - Returns the precision of a type IV point. + Return the precision of a type IV point. This integer is the number of disks used in the approximation of the type IV point. Not defined for type I, II, or III points. @@ -652,7 +652,7 @@ def diameter(self, basepoint=Infinity): def path_distance_metric(self, other): r""" - Returns the path distance metric distance between this point and ``other``. + Return the path distance metric distance between this point and ``other``. Also referred to as the hyperbolic metric, or the big metric. @@ -753,7 +753,7 @@ def Hsia_kernel(self, other, basepoint): def small_metric(self, other): r""" - Returns the small metric distance between this point and ``other``. + Return the small metric distance between this point and ``other``. The small metric is an extension of twice the spherical distance on `P^1(\CC_p)`. @@ -930,7 +930,7 @@ def Hsia_kernel_infinity(self, other): def center(self): r""" - Returns the center of the corresponding disk (or sequence of disks) + Return the center of the corresponding disk (or sequence of disks) in `\CC_p`. OUTPUT: An element of the ``base`` of the parent Berkovich space. @@ -962,7 +962,7 @@ def center(self): def type_of_point(self): r""" - Returns the type of this point of Berkovich space over `\CC_p`. + Return the type of this point of Berkovich space over `\CC_p`. OUTPUT: An integer between 1 and 4 inclusive. @@ -1251,7 +1251,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check def as_projective_point(self): r""" - Returns the corresponding point of projective Berkovich space. + Return the corresponding point of projective Berkovich space. We identify affine Berkovich space with the subset `P^1_{\text{Berk}}(C_p) - \{(1 : 0)\}`. @@ -1349,7 +1349,7 @@ def __eq__(self, other): def __hash__(self): """ - Returns the hash of this point. + Return the hash of this point. EXAMPLES:: @@ -1378,7 +1378,7 @@ def __hash__(self): def lt(self, other): r""" - Returns ``True`` if this point is less than ``other`` in the standard partial order. + Return ``True`` if this point is less than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of the corresponding disks in ``Cp``. @@ -1455,7 +1455,7 @@ def lt(self, other): def gt(self, other): r""" - Returns ``True`` if this point is greater than ``other`` in the standard partial order. + Return ``True`` if this point is greater than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of the corresponding disks in `\CC_p`. @@ -1605,7 +1605,7 @@ def join(self, other, basepoint=Infinity): def involution_map(self): r""" - Returns the image of this point under the involution map. + Return the image of this point under the involution map. The involution map is the extension of the map ``z |-> 1/z`` on `\CC_p` to Berkovich space. @@ -1888,7 +1888,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check def as_affine_point(self): """ - Returns the corresponding affine point after dehomogenizing at infinity. + Return the corresponding affine point after dehomogenizing at infinity. OUTPUT: A point of affine Berkovich space. @@ -1994,7 +1994,7 @@ def __eq__(self, other): def __hash__(self): """ - Returns the hash of this point. + Return the hash of this point. EXAMPLES:: @@ -2024,7 +2024,7 @@ def __hash__(self): def lt(self, other): r""" - Returns ``True`` if this point is less than ``other`` in the standard partial order. + Return ``True`` if this point is less than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of the corresponding disks in `\CC_p`. @@ -2114,7 +2114,7 @@ def lt(self, other): def gt(self, other): r""" - Returns ``True`` if this point is greater than ``other`` in the standard partial order. + Return ``True`` if this point is greater than ``other`` in the standard partial order. Roughly, the partial order corresponds to containment of the corresponding disks in `\CC_p`. @@ -2351,7 +2351,7 @@ def join(self, other, basepoint=Infinity): def involution_map(self): r""" - Returns the image of this point under the involution map. + Return the image of this point under the involution map. The involution map is the extension of the map ``z |-> 1/z`` on `P^1(\CC_p)` to Berkovich space. diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index a4210eff43f..74aea78fdbc 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -117,7 +117,7 @@ def residue_characteristic(self): def is_padic_base(self): """ - Returns ``True`` if this Berkovich space is backed by a p-adic field. + Return ``True`` if this Berkovich space is backed by a p-adic field. OUTPUT: @@ -140,7 +140,7 @@ def is_padic_base(self): def is_number_field_base(self): """ - Returns ``True`` if this Berkovich space is backed by a p-adic field. + Return ``True`` if this Berkovich space is backed by a p-adic field. OUTPUT: From 75e5fe5e012c82f1d584b18cdfa4416e74aacd63 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 17 Aug 2020 16:10:14 -0400 Subject: [PATCH 223/379] changed degree to vertex_property in test --- src/sage/graphs/generic_graph.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 5ca6aa29b06..c032b8a0feb 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10459,7 +10459,8 @@ def vertex_iterator(self, vertices=None, vertex_property=None): :: sage: H = graphs.PathGraph(5) - sage: for v in H.vertex_iterator(degree=1): + sage: degree_one = lambda v: H.degree(v) == 1 + sage: for v in H.vertex_iterator(vertex_property=degree_one): ....: print(v) 0 4 From 6502126fa46496d42309e574321be9e386cf343d Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 17 Aug 2020 18:15:53 -0400 Subject: [PATCH 224/379] 29844: added more examples --- src/sage/schemes/berkovich/berkovich_space.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 74aea78fdbc..8a26d62244f 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -408,6 +408,14 @@ class Berkovich_Cp_Affine(Berkovich_Cp): Element = Berkovich_Element_Cp_Affine def __init__(self, base, ideal=None): + """ + The Python constructor. + + EXAMPLES:: + + sage: Berkovich_Cp_Affine(3) + Affine Berkovich line over Cp(3) of precision 20 + """ if base in ZZ: if base.is_prime(): base = Qp(base) # change to Qpbar @@ -592,6 +600,14 @@ class Berkovich_Cp_Projective(Berkovich_Cp): Element = Berkovich_Element_Cp_Projective def __init__(self, base, ideal=None): + """ + The Python constructor. + + EXAMPLES:: + + sage: Berkovich_Cp_Projective(3) + Projective Berkovich line over Cp(3) of precision 20 + """ if base in ZZ: if base.is_prime(): base = ProjectiveSpace(Qp(base), 1) From 45aeabd06a57edc121f2d13c12224c9b77391717 Mon Sep 17 00:00:00 2001 From: Francis Clarke Date: Tue, 8 Dec 2015 18:49:41 +0000 Subject: [PATCH 225/379] doctests --- src/sage/rings/number_field/number_field.py | 15 +++++++++++++++ .../rings/number_field/number_field_element.pyx | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 359a3d611bf..ed9c935ab6e 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -779,6 +779,21 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name Number Field in a1 with defining polynomial x^2 + 2 over its base field sage: K.base_field().base_field() Number Field in a2 with defining polynomial x^2 + 1 + + LaTeX versions of generator names can be specified either as:: + + sage: K = NumberField([x^3 - 2, x^3 - 3, x^3 - 5], names=['a', 'b', 'c'], latex_names=[r'\alpha', r'\beta', r'\gamma']) + sage: K.inject_variables(verbose=False) + sage: latex(a + b + c) + \alpha + \beta + \gamma + + or as:: + + sage: K = NumberField([x^3 - 2, x^3 - 3, x^3 - 5], names='a', latex_names=r'\alpha') + sage: K.inject_variables() + Defining a0, a1, a2 + sage: latex(a0 + a1 + a2) + \alpha_{0} + \alpha_{1} + \alpha_{2} A bigger tower of quadratic fields:: diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 349247c15fb..1883a9675f9 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -4788,6 +4788,12 @@ cdef class NumberFieldElement_relative(NumberFieldElement): sage: L. = NumberField(y^3 + y + alpha) sage: latex((beta + zeta)^3) # indirect doctest 3 \zeta_{12} \beta^{2} + \left(3 \zeta_{12}^{2} - 1\right) \beta - \alpha + \zeta_{12}^{3} + sage: L. = NumberField(y^3 + y + alpha, latex_name=r'\beta') + sage: latex((b + zeta)^3) # indirect doctest + 3 \zeta_{12} \beta^{2} + \left(3 \zeta_{12}^{2} - 1\right) \beta - \alpha + \zeta_{12}^{3} + sage: M. = L.extension(x^2 - 5, latex_name=r'\gamma') + sage: latex(zeta + c) # indirect doctest + \gamma + \zeta_{12} """ K = self.number_field() R = K.base_field()[K.variable_name()] From f2ffd171ddef3c2f20cfe678cafa8b59c6d289f9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Aug 2020 15:09:05 -0700 Subject: [PATCH 226/379] Ring.extension, NumberField...__init__: Prefer latex_names over latex_name --- src/sage/categories/pushout.py | 4 ++-- .../rings/finite_rings/finite_field_base.pyx | 11 ++++++--- src/sage/rings/number_field/number_field.py | 24 +++++++++++++------ src/sage/rings/ring.pyx | 2 +- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 59078f20879..2fa191d76c5 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3077,11 +3077,11 @@ def _apply_functor(self, R): return R.extension(self.polys[0], names=self.names[0], embedding=self.embeddings[0], structure=self.structures[0], prec=self.precs[0], implementation=self.implementations[0], - latex_name=self.latex_names[0], **self.kwds) + latex_names=self.latex_names[0], **self.kwds) return R.extension(self.polys, names=self.names, embedding=self.embeddings, structure=self.structures, prec=self.precs, implementation=self.implementations, - latex_name=self.latex_names, **self.kwds) + latex_names=self.latex_names, **self.kwds) def __eq__(self, other): """ diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index d1cebcb0580..3884f06a7a6 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1359,8 +1359,11 @@ cdef class FiniteField(Field): - ``modulus`` -- a polynomial with coefficients in ``self``, or an integer. - - ``name`` -- string: the name of the generator in the new - extension + - ``name`` or ``names`` -- string: the name of the generator + in the new extension + + - ``latex_name`` or ``latex_names`` -- string: latex name of + the generator in the new extension - ``map`` -- boolean (default: ``False``): if ``False``, return just the extension `E`; if ``True``, return a pair @@ -1428,6 +1431,8 @@ cdef class FiniteField(Field): from sage.rings.integer import Integer if name is None and names is not None: name = names + if latex_name is None and latex_names is not None: + latex_name = latex_names if self.degree() == 1: if isinstance(modulus, (int, Integer)): E = GF(self.characteristic()**modulus, name=name, **kwds) @@ -1454,7 +1459,7 @@ cdef class FiniteField(Field): except AssertionError: # coercion already exists pass else: - E = Field.extension(self, modulus, name=name, embedding=embedding, **kwds) + E = Field.extension(self, modulus, name=name, embedding=embedding, latex_name=latex_name, **kwds) if map: return (E, E.coerce_map_from(self)) else: diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index ed9c935ab6e..e86a670b52e 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -233,7 +233,9 @@ def proof_flag(t): from sage.rings.real_lazy import RLF, CLF -def NumberField(polynomial, name=None, check=True, names=None, embedding=None, latex_name=None, assume_disc_small=False, maximize_at_primes=None, structure=None, **kwds): +def NumberField(polynomial, name=None, check=True, names=None, embedding=None, + latex_name=None, assume_disc_small=False, maximize_at_primes=None, structure=None, + *, latex_names=None, **kwds): r""" Return *the* number field (or tower of number fields) defined by the irreducible ``polynomial``. @@ -242,13 +244,15 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, l - ``polynomial`` - a polynomial over `\QQ` or a number field, or a list of such polynomials. - - ``name`` - a string or a list of strings, the names of the generators + - ``names`` (or ``name``) - a string or a list of strings, the names of + the generators - ``check`` - a boolean (default: ``True``); do type checking and irreducibility checking. - ``embedding`` - ``None``, an element, or a list of elements, the images of the generators in an ambient field (default: ``None``) - - ``latex_name`` - ``None``, a string, or a list of strings (default: - ``None``), how the generators are printed for latex output + - ``latex_names`` (or ``latex_name``) - ``None``, a string, or a + list of strings (default: ``None``), how the generators are printed + for latex output - ``assume_disc_small`` -- a boolean (default: ``False``); if ``True``, assume that no square of a prime greater than PARI's primelimit (which should be 500000); only applies for absolute fields at @@ -539,6 +543,8 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, l """ if names is not None: name = names + if latex_names is not None: + latex_name = latex_names for key, val in kwds.items(): if key not in ['implementation', 'prec']: raise TypeError("NumberField() got an unexpected keyword argument '%s'"%key) @@ -1558,7 +1564,7 @@ def construction(self): The construction preserves latex variable names:: - sage: K. = NumberField([x^3+x^2+1, x^2+1, x^7+x+1], latex_name=['alpha', 'beta', 'gamma']) + sage: K. = NumberField([x^3+x^2+1, x^2+1, x^7+x+1], latex_names=['alpha', 'beta', 'gamma']) sage: F, R = K.construction() sage: F(R) == K True @@ -5406,7 +5412,7 @@ def elements_of_norm(self, n, proof=None): B = self.pari_bnf(proof).bnfisintnorm(n) return [self(x, check=False) for x in B] - def extension(self, poly, name=None, names=None, latex_name=None, *args, **kwds): + def extension(self, poly, name=None, names=None, latex_name=None, latex_names=None, *args, **kwds): """ Return the relative extension of this field by a given polynomial. @@ -5455,8 +5461,12 @@ def extension(self, poly, name=None, names=None, latex_name=None, *args, **kwds) poly = poly.change_ring(self) if names is not None: name = names - if isinstance(name, tuple): + if isinstance(name, (tuple, list)): name = name[0] + if latex_names is not None: + latex_name = latex_names + if isinstance(latex_name, (tuple, list)): + latex_name = latex_name[0] return NumberField(poly, name, latex_name=latex_name, *args, **kwds) def factor(self, n): diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index a8bcf226c41..cfeaf7425a3 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -1508,7 +1508,7 @@ cdef class CommutativeRing(Ring): if name is None: name = str(poly.parent().gen(0)) for key, val in kwds.items(): - if key not in ['structure', 'implementation', 'prec', 'embedding', 'latex_name']: + if key not in ['structure', 'implementation', 'prec', 'embedding', 'latex_name', 'latex_names']: raise TypeError("extension() got an unexpected keyword argument '%s'"%key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): raise NotImplementedError("ring extension with prescripted %s is not implemented"%key) From e0c216bb35121c5537653800698db3cd51781578 Mon Sep 17 00:00:00 2001 From: Francis Clarke Date: Mon, 7 Dec 2015 11:48:04 +0000 Subject: [PATCH 227/379] Treat latex_names in a similar manner to names --- src/sage/rings/number_field/number_field.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index e86a670b52e..1f98d497f2a 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -828,6 +828,8 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name embeddings = [None] * len(polynomials) if latex_names is None: latex_names = [None] * len(polynomials) + elif isinstance(latex_names, str): + latex_names = ['%s_{%s}' % (latex_names, i) for i in range(len(polynomials))] if structures is None: structures = [None] * len(polynomials) From 86b40c2a3d80f07963f012d502062bcb73b26d28 Mon Sep 17 00:00:00 2001 From: Francis Clarke Date: Sun, 6 Dec 2015 09:48:04 +0000 Subject: [PATCH 228/379] adjust _latex for elements of relative number fields --- src/sage/rings/number_field/number_field_element.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 1883a9675f9..723f30df044 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -4797,6 +4797,7 @@ cdef class NumberFieldElement_relative(NumberFieldElement): """ K = self.number_field() R = K.base_field()[K.variable_name()] + R._latex_names = [K.latex_variable_name()] return R(self.list())._latex_() def charpoly(self, var='x'): From 3e5cc17d9dc779dc214161860e33d2dcf63582a1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Aug 2020 15:38:00 -0700 Subject: [PATCH 229/379] src/sage/categories/pushout.py: Prefer latex_names in doctest --- src/sage/categories/pushout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 2fa191d76c5..3e27a5dc0e4 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3094,12 +3094,12 @@ def __eq__(self, other): sage: F == loads(dumps(F)) True - sage: K2. = NumberField(x^3+x^2+1, latex_name='a') + sage: K2. = NumberField(x^3+x^2+1, latex_names='a') sage: F2 = K2.construction()[0] sage: F2 == F True - sage: K3. = NumberField(x^3+x^2+1, latex_name='alpha') + sage: K3. = NumberField(x^3+x^2+1, latex_names='alpha') sage: F3 = K3.construction()[0] sage: F3 == F False From e40588ababaaeb1899352149d8dcc854fb7bc9a4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Aug 2020 15:38:29 -0700 Subject: [PATCH 230/379] FiniteField.extension: Make arguments latex_name, latex_names keyword-only --- src/sage/rings/finite_rings/finite_field_base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 3884f06a7a6..e3275fe7ce4 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1350,7 +1350,7 @@ cdef class FiniteField(Field): self.base_ring()) def extension(self, modulus, name=None, names=None, map=False, embedding=None, - latex_name=None, **kwds): + *, latex_name=None, latex_names=None, **kwds): """ Return an extension of this finite field. From c598fbbd9730a35ec17cb0c72b3f8f1608fcadf2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Aug 2020 15:38:53 -0700 Subject: [PATCH 231/379] src/sage/rings/number_field/number_field_element.pyx: Use latex_variable_names instead of deprecated latex_variable_name --- src/sage/rings/number_field/number_field_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 723f30df044..02ca74035ce 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -4797,7 +4797,7 @@ cdef class NumberFieldElement_relative(NumberFieldElement): """ K = self.number_field() R = K.base_field()[K.variable_name()] - R._latex_names = [K.latex_variable_name()] + R._latex_names = K.latex_variable_names() return R(self.list())._latex_() def charpoly(self, var='x'): From f8f82c9f29218826b4002844b8ba499815f00cac Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 17 Aug 2020 19:28:30 -0400 Subject: [PATCH 232/379] fixed crashing and shortened docstring --- src/sage/graphs/generic_graph.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c032b8a0feb..290530e444f 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10430,9 +10430,9 @@ def vertex_iterator(self, vertices=None, vertex_property=None): - ``vertices`` -- iterated vertices are these intersected with the vertices of the (di)graph - - ``vertex_property`` -- function (default: ``None``); a function that inputs - a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if - ``vertex_property(v) == True`` + - ``vertex_property`` -- function (default: ``None``); a function + that inputs a vertex and outputs a boolean value, i.e., a vertex + ``v`` is kept if ``vertex_property(v) == True`` EXAMPLES:: @@ -10478,8 +10478,8 @@ def vertex_iterator(self, vertices=None, vertex_property=None): for v in self._backend.iterator_verts(vertices): if vertex_property(v): yield v - - return self._backend.iterator_verts(vertices) + else: + yield from self._backend.iterator_verts(vertices) __iter__ = vertex_iterator From d0bc7863a60366121688bb09f64af3618d3e1edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 19 Aug 2020 10:52:30 +1200 Subject: [PATCH 233/379] transform python test into sage tests - step one. --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 54 ++++++------ src/sage/rings/polynomial/pbori/addition.py | 56 ++++++------ src/sage/rings/polynomial/pbori/cnf.py | 88 +++++++++---------- src/sage/rings/polynomial/pbori/context.py | 22 ++--- .../polynomial/pbori/easy_polynomials.py | 16 ++-- src/sage/rings/polynomial/pbori/fglm.py | 48 +++++----- src/sage/rings/polynomial/pbori/frontend.py | 24 ++--- src/sage/rings/polynomial/pbori/gbcore.py | 32 +++---- src/sage/rings/polynomial/pbori/intersect.py | 10 +-- src/sage/rings/polynomial/pbori/intpolys.py | 14 +-- src/sage/rings/polynomial/pbori/ll.py | 50 +++++------ src/sage/rings/polynomial/pbori/nf.py | 14 +-- src/sage/rings/polynomial/pbori/parallel.py | 62 ++++++------- src/sage/rings/polynomial/pbori/parsegat.py | 2 +- src/sage/rings/polynomial/pbori/plot.py | 10 +-- src/sage/rings/polynomial/pbori/randompoly.py | 12 +-- 16 files changed, 257 insertions(+), 257 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index 7dccea1c8e2..d43832d4e1e 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -8,57 +8,57 @@ Examples: - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) - >>> x0>x1 + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) + sage: x0>x1 True - >>> x0>x1*x2 + sage: x0>x1*x2 True - >>> y0>y1 + sage: y0>y1 True - >>> y0>y1*y2 + sage: y0>y1*y2 True - >>> r = r.clone(ordering=dlex) - >>> r(x0) > r(x1) + sage: r = r.clone(ordering=dlex) + sage: r(x0) > r(x1) True - >>> r(x0) > r(x1*x2) + sage: r(x0) > r(x1*x2) False - >>> r = r.clone(ordering=dp_asc) - >>> r(x0) > r(x1) + sage: r = r.clone(ordering=dp_asc) + sage: r(x0) > r(x1) False - >>> r(x0) > r(x1*x2) + sage: r(x0) > r(x1*x2) False - >>> r = r.clone(ordering=block_dlex, blocks=[3]) - >>> r(x0) > r(x1) + sage: r = r.clone(ordering=block_dlex, blocks=[3]) + sage: r(x0) > r(x1) True - >>> r(x0) > r(x1*x2) + sage: r(x0) > r(x1*x2) False - >>> r(x0) > r(y0*y1*y2) + sage: r(x0) > r(y0*y1*y2) True - >>> r = r.clone(ordering=block_dp_asc) - >>> r(x0) > r(x1) + sage: r = r.clone(ordering=block_dp_asc) + sage: r(x0) > r(x1) False - >>> r(x0) > r(y0) + sage: r(x0) > r(y0) False - >>> r(x0) > r(x1*x2) + sage: r(x0) > r(x1*x2) False - >>> r = r.clone(ordering=block_dp_asc, blocks=[3]) - >>> r(x0) > r(y0) + sage: r = r.clone(ordering=block_dp_asc, blocks=[3]) + sage: r(x0) > r(y0) True - >>> r(x0) > r(y0*y1) + sage: r(x0) > r(y0*y1) True - >>> r = r.clone(names=["z17", "z7"]) - >>> [r.variable(idx) for idx in xrange(3)] + sage: r = r.clone(names=["z17", "z7"]) + sage: [r.variable(idx) for idx in xrange(3)] [z17, z7, x2] - >>> r = r.clone(names="abcde") - >>> [r.variable(idx) for idx in xrange(6)] + sage: r = r.clone(names="abcde") + sage: [r.variable(idx) for idx in xrange(6)] [a, b, c, d, e, y2] """ diff --git a/src/sage/rings/polynomial/pbori/addition.py b/src/sage/rings/polynomial/pbori/addition.py index 5b142415888..54a61408c35 100644 --- a/src/sage/rings/polynomial/pbori/addition.py +++ b/src/sage/rings/polynomial/pbori/addition.py @@ -6,11 +6,11 @@ def add_bits_old(bits): """Adds n bits - >>> from sage.rings.polynomial.pbori.brial import * - >>> r=Ring(10) - >>> add_bits_old([r.variable(i) for i in xrange(3)]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(10) + sage: add_bits_old([r.variable(i) for i in xrange(3)]) [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] - >>> add_bits_old([r.variable(i) for i in xrange(4)]) + sage: add_bits_old([r.variable(i) for i in xrange(4)]) [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3)] """ bits = list(bits) @@ -33,13 +33,13 @@ def add_bits_old(bits): def add_bits(bits): """Adds n bit variables, by Lucas theorem - >>> from sage.rings.polynomial.pbori.brial import * - >>> r=Ring(10) - >>> add_bits([r.variable(i) for i in xrange(3)]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(10) + sage: add_bits([r.variable(i) for i in xrange(3)]) [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] - >>> add_bits([r.variable(i) for i in xrange(4)]) + sage: add_bits([r.variable(i) for i in xrange(4)]) [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3), x(0)*x(1)*x(2)*x(3)] - >>> add_bits([r.variable(0)]) + sage: add_bits([r.variable(0)]) [x(0)] """ bits = list(bits) @@ -59,15 +59,15 @@ def add_bits(bits): def add_bit_expressions(bit_expressions): """Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. - >>> from sage.rings.polynomial.pbori.brial import * - >>> r=Ring(20) - >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(20) + sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] - >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) + sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] - >>> add_bit_expressions([r.variable(11), r.variable(11)]) + sage: add_bit_expressions([r.variable(11), r.variable(11)]) [0, x(11)] - >>> add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) + sage: add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] """ @@ -85,16 +85,16 @@ def add_bit_expressions(bit_expressions): def add_words(words): """def adds n words, this words are supposed to consists of list of their bits. - >>> from sage.rings.polynomial.pbori.brial import * - >>> r=Ring(1000) - >>> add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(1000) + sage: add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] - >>> res=add_words([[r.variable(100+i*9+j) for i in xrange(4)] for j in xrange(9)]) - >>> [len(p) for p in res] + sage: res=add_words([[r.variable(100+i*9+j) for i in xrange(4)] for j in xrange(9)]) + sage: [len(p) for p in res] [9, 45, 495, 12870, 735462, 70285482, 1891358892, 6435] - >>> [p.deg() for p in res] + sage: [p.deg() for p in res] [1, 2, 4, 8, 12, 18, 25, 33] - >>> [p.n_nodes() for p in res] + sage: [p.n_nodes() for p in res] [9, 25, 54, 100, 153, 211, 249, 100] """ @@ -112,12 +112,12 @@ def add_words(words): def multiply_by_addition(word_a, word_b): """Multiply two words - >>> from sage.rings.polynomial.pbori.brial import Ring - >>> r=Ring(1000) - >>> x = r.variable - >>> n=7 - >>> res=multiply_by_addition([x(200+2*i) for i in xrange(n)], [x(200+2*i+1) for i in xrange(n)]) - >>> [p.n_nodes() for p in res] + sage: from sage.rings.polynomial.pbori.brial import Ring + sage: r=Ring(1000) + sage: x = r.variable + sage: n=7 + sage: res=multiply_by_addition([x(200+2*i) for i in xrange(n)], [x(200+2*i+1) for i in xrange(n)]) + sage: [p.n_nodes() for p in res] [2, 4, 7, 17, 38, 85, 222, 630, 1358, 1702, 1713, 1430, 875, 214, 0] """ word_a = list(word_a) diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py index f57d38894a9..c3fd41c068f 100644 --- a/src/sage/rings/polynomial/pbori/cnf.py +++ b/src/sage/rings/polynomial/pbori/cnf.py @@ -14,10 +14,10 @@ def __init__(self, r, random_seed=16): def zero_blocks(self, f): """divides the zero set of f into blocks - >>> from sage.rings.polynomial.pbori.brial import * - >>> r = declare_ring(["x", "y", "z"], dict()) - >>> e = CNFEncoder(r) - >>> e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) [{y: 0}, {z: 0}, {x: 0}] """ f = Polynomial(f) @@ -78,17 +78,17 @@ def get_val(var): def clauses(self, f): """ - >>> from sage.rings.polynomial.pbori.brial import * - >>> r = declare_ring(["x", "y", "z"], dict()) - >>> e = CNFEncoder(r) - >>> e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS [{...x: 0...}] - >>> e.clauses(r.variable(1)+r.variable(0)) # doctest:+ELLIPSIS + sage: e.clauses(r.variable(1)+r.variable(0)) # doctest:+ELLIPSIS [{...x: 1...}, {...y: 1...}] - >>> [sorted(c.iteritems()) for c in e.clauses(r.variable(0)*r.variable(1)*r.variable(2))] + sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(0)*r.variable(1)*r.variable(2))] [[(z, 0), (y, 0), (x, 0)]] - >>> [sorted(c.iteritems()) for c in e.clauses(r.variable(1)+r.variable(0))] + sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(1)+r.variable(0))] [[(y, 1), (x, 0)], [(y, 0), (x, 1)]] """ f_plus_one = f + 1 @@ -102,14 +102,14 @@ def clauses(self, f): def polynomial_clauses(self, f): """ - >>> from sage.rings.polynomial.pbori.brial import * - >>> r = declare_ring(["x", "y", "z"], dict()) - >>> e = CNFEncoder(r) - >>> e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) [x*y*z] - >>> v = r.variable - >>> p = v(1)*v(2)+v(2)*v(0)+1 - >>> groebner_basis([p], heuristic = False)==groebner_basis(e.polynomial_clauses(p), heuristic = False) + sage: v = r.variable + sage: p = v(1)*v(2)+v(2)*v(0)+1 + sage: groebner_basis([p], heuristic = False)==groebner_basis(e.polynomial_clauses(p), heuristic = False) True """ @@ -141,11 +141,11 @@ def get_sign(val): def dimacs_encode_polynomial(self, p): """ - >>> from sage.rings.polynomial.pbori.brial import * - >>> d=dict() - >>> r = declare_ring(["x", "y", "z"], d) - >>> e = CNFEncoder(r) - >>> e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: d=dict() + sage: r = declare_ring(["x", "y", "z"], d) + sage: e = CNFEncoder(r) + sage: e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]) ['1 2 -3 0', '1 -2 3 0', '-1 -2 -3 0', '-1 2 3 0'] """ clauses = self.clauses(p) @@ -156,14 +156,14 @@ def dimacs_encode_polynomial(self, p): def dimacs_cnf(self, polynomial_system): r""" - >>> from sage.rings.polynomial.pbori.brial import * - >>> r = declare_ring(["x", "y", "z"], dict()) - >>> e = CNFEncoder(r) - >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0' - >>> e.dimacs_cnf([r.variable(1)+r.variable(0)]) + sage: e.dimacs_cnf([r.variable(1)+r.variable(0)]) 'c cnf generated by PolyBoRi\np cnf 3 2\n1 -2 0\n-1 2 0' - >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) + sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n-1 2 0\n1 -2 0' """ clauses_list = [c for p in polynomial_system for c in self. @@ -182,18 +182,18 @@ class CryptoMiniSatEncoder(CNFEncoder): def dimacs_encode_polynomial(self, p): r""" - >>> from sage.rings.polynomial.pbori.brial import * - >>> d=dict() - >>> r = declare_ring(["x", "y", "z"], d) - >>> e = CryptoMiniSatEncoder(r) - >>> p = d["x"]+d["y"]+d["z"] - >>> p.deg() + sage: from sage.rings.polynomial.pbori.brial import * + sage: d=dict() + sage: r = declare_ring(["x", "y", "z"], d) + sage: e = CryptoMiniSatEncoder(r) + sage: p = d["x"]+d["y"]+d["z"] + sage: p.deg() 1 - >>> len(p) + sage: len(p) 3 - >>> e.dimacs_encode_polynomial(p) + sage: e.dimacs_encode_polynomial(p) ['x1 2 3 0\nc g 1 x + y + z'] - >>> e.dimacs_encode_polynomial(p+1) + sage: e.dimacs_encode_polynomial(p+1) ['x1 2 -3 0\nc g 2 x + y + z + 1'] """ if p.deg() != 1 or len(p) <= 1: @@ -216,14 +216,14 @@ def dimacs_encode_polynomial(self, p): def dimacs_cnf(self, polynomial_system): r""" - >>> from sage.rings.polynomial.pbori.brial import * - >>> r = declare_ring(["x", "y", "z"], dict()) - >>> e = CryptoMiniSatEncoder(r) - >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CryptoMiniSatEncoder(r) + sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0\nc g 1 x*y*z\nc v 1 x\nc v 2 y\nc v 3 z' - >>> e.dimacs_cnf([r.variable(1)+r.variable(0)]) + sage: e.dimacs_cnf([r.variable(1)+r.variable(0)]) 'c cnf generated by PolyBoRi\np cnf 3 1\nx1 2 0\nc g 2 x + y\nc v 1 x\nc v 2 y' - >>> e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) + sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) 'c cnf generated by PolyBoRi\np cnf 3 2\n-1 -2 -3 0\nc g 3 x*y*z\nx1 2 0\nc g 4 x + y\nc v 1 x\nc v 2 y\nc v 3 z' """ uv = list(used_vars_set(polynomial_system).variables()) diff --git a/src/sage/rings/polynomial/pbori/context.py b/src/sage/rings/polynomial/pbori/context.py index b1d9301d1c9..4c13f59ac73 100644 --- a/src/sage/rings/polynomial/pbori/context.py +++ b/src/sage/rings/polynomial/pbori/context.py @@ -7,7 +7,7 @@ def _exists(): """PolyBoRi convention: checking optional components for prerequisites here - >>> _exists() + sage: _exists() True """ from distutils.sysconfig import get_python_version @@ -24,13 +24,13 @@ class FactoryContext(object): callable object. It is useful together with the with statement. Example: - >>> r = Ring(1000) - >>> from sage.rings.polynomial.pbori.brial import Variable - >>> def var(idx): return Variable(idx, r) - >>> with FactoryContext(Variable, var): + sage: r = Ring(1000) + sage: from sage.rings.polynomial.pbori.brial import Variable + sage: def var(idx): return Variable(idx, r) + sage: with FactoryContext(Variable, var): ... print Variable(17) x(17) - >>> try: + sage: try: ... print Variable(17) ... except: ... print "caught expected exception" @@ -64,14 +64,14 @@ class RingContext(object): the with statement. Example: - >>> r = Ring(1000) - >>> from sage.rings.polynomial.pbori.brial import Variable - >>> print Variable(17, r) + sage: r = Ring(1000) + sage: from sage.rings.polynomial.pbori.brial import Variable + sage: print Variable(17, r) x(17) - >>> with RingContext(r): + sage: with RingContext(r): ... print Variable(17), Monomial(), Polynomial(0), BooleSet() x(17) 1 0 {} - >>> try: + sage: try: ... print Variable(17) ... except: ... print "caught expected exception" diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py index 28c72f18324..c788bb77602 100644 --- a/src/sage/rings/polynomial/pbori/easy_polynomials.py +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -5,12 +5,12 @@ def easy_linear_polynomials(p): """ Get linear polynomials implied by given polynomial. - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> easy_linear_polynomials(x(1)*x(2) + 1) + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: easy_linear_polynomials(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] - >>> easy_linear_polynomials(x(1)*x(2) + 0) + sage: easy_linear_polynomials(x(1)*x(2) + 0) [] - >>> easy_linear_polynomials(x(0)*x(1) + x(0)*x(2) + 1) + sage: easy_linear_polynomials(x(0)*x(1) + x(0)*x(2) + 1) [x(0) + 1, x(1) + x(2) + 1] """ res = [] @@ -28,12 +28,12 @@ def easy_linear_polynomials_via_interpolation(p): """ Get linear polynomials implied by given polynomial using interpolation of the variety. - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] - >>> easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) + sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) [] - >>> easy_linear_polynomials_via_interpolation(x(0)*x(1) + x(0)*x(2) + 1) + sage: easy_linear_polynomials_via_interpolation(x(0)*x(1) + x(0)*x(2) + 1) [x(0) + 1, x(1) + x(2) + 1] """ res = [] diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index 5ab560294be..7fb85daab71 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -25,14 +25,14 @@ def fglm(I, from_ring, to_ring): converts *reduced* Groebner Basis in from_ring to a GroebnerBasis in to_ring. It acts independend of the global ring, which is restored at the end of the computation, - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode - >>> dp_asc = OrderCode.dp_asc - >>> r=declare_ring(['x','y','z'],dict()) - >>> old_ring = r - >>> new_ring = old_ring.clone(ordering=dp_asc) - >>> (x,y,z) = [old_ring.variable(i) for i in xrange(3)] - >>> ideal=[x+z, y+z]# lp Groebner basis - >>> list(fglm(ideal, old_ring, new_ring)) + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: dp_asc = OrderCode.dp_asc + sage: r=declare_ring(['x','y','z'],dict()) + sage: old_ring = r + sage: new_ring = old_ring.clone(ordering=dp_asc) + sage: (x,y,z) = [old_ring.variable(i) for i in xrange(3)] + sage: ideal=[x+z, y+z]# lp Groebner basis + sage: list(fglm(ideal, old_ring, new_ring)) [y + x, z + x] """ for poly in I: @@ -44,13 +44,13 @@ def fglm(I, from_ring, to_ring): def vars_real_divisors(monomial, monomial_set): """ returns all elements of of monomial_set, which result multiplied by a variable in monomial. - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode - >>> dp_asc = OrderCode.dp_asc - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring - >>> r=Ring(1000) - >>> x = r.variable - >>> b=BooleSet([x(1)*x(2),x(2)]) - >>> vars_real_divisors(x(1)*x(2)*x(3),b) + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: dp_asc = OrderCode.dp_asc + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: r=Ring(1000) + sage: x = r.variable + sage: b=BooleSet([x(1)*x(2),x(2)]) + sage: vars_real_divisors(x(1)*x(2)*x(3),b) {{x(1),x(2)}} """ return BooleSet(Polynomial(monomial_set.divisors_of(monomial)). \ @@ -60,16 +60,16 @@ def vars_real_divisors(monomial, monomial_set): def m_k_plus_one(completed_elements, variables): """ calculates $m_{k+1}$ from the FGLM algorithm as described in Wichmanns diploma thesis It would be nice to be able to efficiently extract the smallest term of a polynomial - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode - >>> dp_asc = OrderCode.dp_asc - >>> r=Ring(1000) - >>> x = r.variable - >>> s=BooleSet([x(1)*x(2),x(1),x(2),Monomial(r),x(3)]) - >>> variables=BooleSet([x(1),x(2),x(3)]) - >>> m_k_plus_one(s,variables) + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: dp_asc = OrderCode.dp_asc + sage: r=Ring(1000) + sage: x = r.variable + sage: s=BooleSet([x(1)*x(2),x(1),x(2),Monomial(r),x(3)]) + sage: variables=BooleSet([x(1),x(2),x(3)]) + sage: m_k_plus_one(s,variables) x(2)*x(3) - >>> r2 = r.clone(ordering=dp_asc) - >>> m_k_plus_one(r2(s).set(),r2(variables).set()) + sage: r2 = r.clone(ordering=dp_asc) + sage: m_k_plus_one(r2(s).set(),r2(variables).set()) x(1)*x(3) """ return sorted(completed_elements.cartesian_product(variables).diff( diff --git a/src/sage/rings/polynomial/pbori/frontend.py b/src/sage/rings/polynomial/pbori/frontend.py index b6cde7663ab..6bf7ca0a1ec 100644 --- a/src/sage/rings/polynomial/pbori/frontend.py +++ b/src/sage/rings/polynomial/pbori/frontend.py @@ -4,27 +4,27 @@ a given context. ->>> x(0) +sage: x(0) x(0) ->>> x(0)*x(0) +sage: x(0)*x(0) x(0) ->>> x(0) + x(0) +sage: x(0) + x(0) 0 ->>> x(9999) +sage: x(9999) x(9999) ->>> x(9999)*x(9999) +sage: x(9999)*x(9999) x(9999) ->>> x(9999) + x(9999) +sage: x(9999) + x(9999) 0 ->>> from sage.rings.polynomial.pbori.brial.frontend import * ->>> context = dict(globals()) ->>> polybori_start(context) # doctest: +ELLIPSIS +sage: from sage.rings.polynomial.pbori.brial.frontend import * +sage: context = dict(globals()) +sage: polybori_start(context) # doctest: +ELLIPSIS ipbori... ->>> r = context['declare_ring']('abc') ->>> context['a'] +sage: r = context['declare_ring']('abc') +sage: context['a'] a ->>> r.variable(0) +sage: r.variable(0) a """ diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index bc65df909aa..f187abcea87 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -339,27 +339,27 @@ def ll_constants_pre(I): def variety_size_from_gb(I): """ - >>> r=Ring(100) - >>> x = r.variable - >>> variety_size_from_gb([]) + sage: r=Ring(100) + sage: x = r.variable + sage: variety_size_from_gb([]) 1 - >>> variety_size_from_gb([Polynomial(0, r)]) + sage: variety_size_from_gb([Polynomial(0, r)]) 1 - >>> variety_size_from_gb([Polynomial(1, r)]) + sage: variety_size_from_gb([Polynomial(1, r)]) 0.0 - >>> variety_size_from_gb([x(1)]) + sage: variety_size_from_gb([x(1)]) 1.0 - >>> variety_size_from_gb([x(1), x(2)]) + sage: variety_size_from_gb([x(1), x(2)]) 1.0 - >>> variety_size_from_gb([x(1), x(2)*x(3)]) + sage: variety_size_from_gb([x(1), x(2)*x(3)]) 3.0 - >>> variety_size_from_gb([x(1), x(1)*x(4), x(2)*x(3)]) + sage: variety_size_from_gb([x(1), x(1)*x(4), x(2)*x(3)]) 6.0 - >>> variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) + sage: variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) 5.0 - >>> mons = [Monomial([r.variable(i) for i in xrange(100) if i!=j])\ + sage: mons = [Monomial([r.variable(i) for i in xrange(100) if i!=j])\ for j in xrange(100)] - >>> variety_size_from_gb(mons) + sage: variety_size_from_gb(mons) 1.2676506002282294e+30 """ I = [Polynomial(p) for p in I] @@ -387,10 +387,10 @@ def variety_size_from_gb(I): def other_ordering_pre(I, option_set, kwds): """ - >>> from sage.rings.polynomial.pbori.brial.blocks import declare_ring - >>> r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) - >>> id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] - >>> groebner_basis(id) + sage: from sage.rings.polynomial.pbori.brial.blocks import declare_ring + sage: r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) + sage: id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] + sage: groebner_basis(id) [1] """ diff --git a/src/sage/rings/polynomial/pbori/intersect.py b/src/sage/rings/polynomial/pbori/intersect.py index 0548d18ea70..c40c8462545 100644 --- a/src/sage/rings/polynomial/pbori/intersect.py +++ b/src/sage/rings/polynomial/pbori/intersect.py @@ -16,11 +16,11 @@ def intersect(i, j, **gb_opts): This functions intersects two ideals. The first ring variable is used as helper variable for this intersection. It is assumed, that it doesn't occur in the ideals, and that we have an elimination ordering for this variables. Both assumptions are checked. - >>> from sage.rings.polynomial.pbori.brial.frontend import declare_ring - >>> from sage.rings.polynomial.pbori.brial import Block - >>> r=declare_ring(Block("x", 1000), globals()) - >>> x = r.variable - >>> intersect([x(1),x(2)+1],[x(1),x(2)]) + sage: from sage.rings.polynomial.pbori.brial.frontend import declare_ring + sage: from sage.rings.polynomial.pbori.brial import Block + sage: r=declare_ring(Block("x", 1000), globals()) + sage: x = r.variable + sage: intersect([x(1),x(2)+1],[x(1),x(2)]) [x(1)] """ if not i or not j: diff --git a/src/sage/rings/polynomial/pbori/intpolys.py b/src/sage/rings/polynomial/pbori/intpolys.py index c90f3037743..7304deca98e 100644 --- a/src/sage/rings/polynomial/pbori/intpolys.py +++ b/src/sage/rings/polynomial/pbori/intpolys.py @@ -33,16 +33,16 @@ def __coerce__(self, other): def __add__(self, other): """ - >>> from sage.rings.polynomial.pbori.brial import * - >>> r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS - >>> p=IntegerPolynomial(x(1)) - >>> p + sage: from sage.rings.polynomial.pbori.brial import * + sage: r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS + sage: p=IntegerPolynomial(x(1)) + sage: p {0: x(1)} - >>> p=p+p;p + sage: p=p+p;p {1: x(1)} - >>> p=p+x(2); p + sage: p=p+x(2); p {0: x(2), 1: x(1)} - >>> p+p + sage: p+p {1: x(2), 2: x(1)} """ if not isinstance(other, IntegerPolynomial): diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index bab9536ac3d..b3a3f5a3205 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -209,21 +209,21 @@ def llnf(p): class RingMap(object): """Define a mapping between two rings by common variable names. - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> to_ring = declare_ring([Block("x", 10)], globals()) - >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - >>> mapping = RingMap(to_ring, from_ring) - >>> (x(1)+1).navigation().value() + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: (x(1)+1).navigation().value() 6 - >>> mapping(x(1)+1) + sage: mapping(x(1)+1) x(1) + 1 - >>> mapping(x(1)+1).navigation().value() + sage: mapping(x(1)+1).navigation().value() 1 - >>> mapping.invert(mapping(x(1)+1)) + sage: mapping.invert(mapping(x(1)+1)) x(1) + 1 - >>> mapping.invert(mapping(x(1)+1)).navigation().value() + sage: mapping.invert(mapping(x(1)+1)).navigation().value() 6 - >>> mapping(y(1)+1) + sage: mapping(y(1)+1) Traceback (most recent call last): ... RuntimeError: Operands come from different manager. @@ -232,11 +232,11 @@ class RingMap(object): def __init__(self, to_ring, from_ring): """Initialize map by two given rings. - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> to_ring = declare_ring([Block("x", 10)], globals()) - >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - >>> mapping = RingMap(to_ring, from_ring) - >>> mapping(x(1)+1) + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: mapping(x(1)+1) x(1) + 1 """ @@ -268,11 +268,11 @@ def indices(vars): def __call__(self, poly): """Execute the map to change rings. - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> to_ring = declare_ring([Block("x", 10)], globals()) - >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - >>> mapping = RingMap(to_ring, from_ring) - >>> mapping(x(1)+1) + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: mapping(x(1)+1) x(1) + 1 """ return substitute_variables(self.to_ring, self.to_map, poly) @@ -280,11 +280,11 @@ def __call__(self, poly): def invert(self, poly): """Inverted map to initial ring. - >>> from sage.rings.polynomial.pbori.brial.frontend import * - >>> to_ring = declare_ring([Block("x", 10)], globals()) - >>> from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - >>> mapping = RingMap(to_ring, from_ring) - >>> mapping.invert(mapping(x(1)+1)) + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: mapping.invert(mapping(x(1)+1)) x(1) + 1 """ return substitute_variables(self.from_ring, self.from_map, poly) diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index ef99c9afda0..f270061a1ba 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -84,9 +84,9 @@ def build_and_print_matrices(v, strat): def multiply_polynomials(l, ring): """ - >>> r=Ring(1000) - >>> x=r.variable - >>> multiply_polynomials([x(3), x(2)+x(5)*x(6), x(0), x(0)+1], r) + sage: r=Ring(1000) + sage: x=r.variable + sage: multiply_polynomials([x(3), x(2)+x(5)*x(6), x(0), x(0)+1], r) 0 """ l = [Polynomial(p) for p in l] @@ -643,11 +643,11 @@ def symmGB_F2_C(G, opt_exchange=True, def normal_form(poly, ideal, reduced=True): """ Simple normal form computation of a polynomial against an ideal. - >>> from sage.rings.polynomial.pbori.brial import declare_ring, normal_form - >>> r=declare_ring(['x','y'], globals()) - >>> normal_form(x+y, [y],reduced=True) + sage: from sage.rings.polynomial.pbori.brial import declare_ring, normal_form + sage: r=declare_ring(['x','y'], globals()) + sage: normal_form(x+y, [y],reduced=True) x - >>> normal_form(x+y,[x,y]) + sage: normal_form(x+y,[x,y]) 0 """ ring = poly.ring() diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 4accb47b6a5..83b4c00fce9 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -40,31 +40,31 @@ def to_fast_pickable(l): Each code c refers to the c-2-th position in the conversion list, if c >=2, else to the corresponding Boolean constant if c in {0, 1} EXAMPLES: - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring - >>> r=Ring(1000) - >>> x=r.variable - >>> to_fast_pickable([Polynomial(1, r)]) + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: r=Ring(1000) + sage: x=r.variable + sage: to_fast_pickable([Polynomial(1, r)]) [[1], []] - >>> to_fast_pickable([Polynomial(0, r)]) + sage: to_fast_pickable([Polynomial(0, r)]) [[0], []] - >>> to_fast_pickable([x(0)]) + sage: to_fast_pickable([x(0)]) [[2], [(0, 1, 0)]] - >>> to_fast_pickable([x(0)*x(1)+x(1)]) + sage: to_fast_pickable([x(0)*x(1)+x(1)]) [[2], [(0, 3, 3), (1, 1, 0)]] - >>> to_fast_pickable([x(1)]) + sage: to_fast_pickable([x(1)]) [[2], [(1, 1, 0)]] - >>> to_fast_pickable([x(0)+1]) + sage: to_fast_pickable([x(0)+1]) [[2], [(0, 1, 1)]] - >>> to_fast_pickable([x(0)*x(1)]) + sage: to_fast_pickable([x(0)*x(1)]) [[2], [(0, 3, 0), (1, 1, 0)]] - >>> to_fast_pickable([x(0)*x(1)+x(1)]) + sage: to_fast_pickable([x(0)*x(1)+x(1)]) [[2], [(0, 3, 3), (1, 1, 0)]] - >>> to_fast_pickable([x(0)*x(1)+x(2)]) + sage: to_fast_pickable([x(0)*x(1)+x(2)]) [[2], [(0, 3, 4), (1, 1, 0), (2, 1, 0)]] - >>> p=x(5)*x(23) + x(5)*x(24)*x(59) + x(5) + x(6)*x(23)*x(89) + x(6)*x(60)*x(89) + x(23) + x(24)*x(89) + x(24) + x(60)*x(89) + x(89) + 1 - >>> from_fast_pickable(to_fast_pickable([p]), r)==[p] + sage: p=x(5)*x(23) + x(5)*x(24)*x(59) + x(5) + x(6)*x(23)*x(89) + x(6)*x(60)*x(89) + x(23) + x(24)*x(89) + x(24) + x(60)*x(89) + x(89) + 1 + sage: from_fast_pickable(to_fast_pickable([p]), r)==[p] True - >>> to_fast_pickable([x(0)*x(1), Polynomial(0, r), Polynomial(1, r), x(3)]) + sage: to_fast_pickable([x(0)*x(1), Polynomial(0, r), Polynomial(1, r), x(3)]) [[2, 0, 1, 4], [(0, 3, 0), (1, 1, 0), (3, 1, 0)]] """ if len(l) == 0: @@ -109,26 +109,26 @@ def from_fast_pickable(l, r): OUTPUT: a list of Boolean polynomials EXAMPLES: - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring - >>> r=Ring(1000) - >>> x = r.variable - >>> from_fast_pickable([[1], []], r) + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: r=Ring(1000) + sage: x = r.variable + sage: from_fast_pickable([[1], []], r) [1] - >>> from_fast_pickable([[0], []], r) + sage: from_fast_pickable([[0], []], r) [0] - >>> from_fast_pickable([[2], [(0, 1, 0)]], r) + sage: from_fast_pickable([[2], [(0, 1, 0)]], r) [x(0)] - >>> from_fast_pickable([[2], [(1, 1, 0)]], r) + sage: from_fast_pickable([[2], [(1, 1, 0)]], r) [x(1)] - >>> from_fast_pickable([[2], [(0, 1, 1)]], r) + sage: from_fast_pickable([[2], [(0, 1, 1)]], r) [x(0) + 1] - >>> from_fast_pickable([[2], [(0, 3, 0), (1, 1, 0)]], r) + sage: from_fast_pickable([[2], [(0, 3, 0), (1, 1, 0)]], r) [x(0)*x(1)] - >>> from_fast_pickable([[2], [(0, 3, 3), (1, 1, 0)]], r) + sage: from_fast_pickable([[2], [(0, 3, 3), (1, 1, 0)]], r) [x(0)*x(1) + x(1)] - >>> from_fast_pickable([[2], [(0, 3, 4), (1, 1, 0), (2, 1, 0)]], r) + sage: from_fast_pickable([[2], [(0, 3, 4), (1, 1, 0), (2, 1, 0)]], r) [x(0)*x(1) + x(2)] - >>> from_fast_pickable([[2, 0, 1, 4], [(0, 3, 0), (1, 1, 0), (3, 1, 0)]], r) + sage: from_fast_pickable([[2, 0, 1, 4], [(0, 3, 0), (1, 1, 0), (3, 1, 0)]], r) [x(0)*x(1), 0, 1, x(3)] """ i2poly = {0: r.zero(), 1: r.one()} @@ -257,10 +257,10 @@ def groebner_basis_first_finished(I, *l): - tries to compute groebner_basis(I, **kwd) for kwd in l - returns the result of the first terminated computation EXAMPLES: - >>> from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring - >>> r=Ring(1000) - >>> ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] - >>> #groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: r=Ring(1000) + sage: ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] + sage: #groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) [x(1), x(2)] """ if not I: diff --git a/src/sage/rings/polynomial/pbori/parsegat.py b/src/sage/rings/polynomial/pbori/parsegat.py index b71eb3b3955..436978a0882 100644 --- a/src/sage/rings/polynomial/pbori/parsegat.py +++ b/src/sage/rings/polynomial/pbori/parsegat.py @@ -10,7 +10,7 @@ def _exists(): """PolyBoRi convention: checking optional components for prerequisites here - >>> _exists() + sage: _exists() True """ try: diff --git a/src/sage/rings/polynomial/pbori/plot.py b/src/sage/rings/polynomial/pbori/plot.py index 9364d2bcbe5..dfdca8cb8d4 100644 --- a/src/sage/rings/polynomial/pbori/plot.py +++ b/src/sage/rings/polynomial/pbori/plot.py @@ -13,7 +13,7 @@ def _exists(): """PolyBoRi convention: checking optional components for prerequisites here - >>> _exists() + sage: _exists() True """ try: @@ -133,10 +133,10 @@ def plot(p, filename, colored=True, format="png", EXAMPLES: - >>> r=Ring(1000) - >>> x = r.variable - >>> plot(x(1)+x(0),"/dev/null", colored=True) - >>> plot(x(1)+x(0),"/dev/null", colored=False) + sage: r=Ring(1000) + sage: x = r.variable + sage: plot(x(1)+x(0),"/dev/null", colored=True) + sage: plot(x(1)+x(0),"/dev/null", colored=False) """ THICK_PEN = 5 highlight_path = dict() diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index 0d7655222bf..8eecf6cf693 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -35,12 +35,12 @@ def sparse_random_system(ring, number_of_polynomials, generates a system, which is sparse in the sense, that each polynomial contains only a small subset of variables. In each variable that occurrs in a polynomial it is dense in the terms up to the given degree (every term occurs with probability 1/2). The system will be satisfiable by at least one solution. - >>> from sage.rings.polynomial.pbori.brial import * - >>> r=Ring(10) - >>> s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) - >>> [p.deg() for p in s] + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(10) + sage: s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) + sage: [p.deg() for p in s] [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - >>> sorted(groebner_basis(s), reverse=True) + sage: sorted(groebner_basis(s), reverse=True) [x(0), x(1), x(2), x(3), x(4) + 1, x(5), x(6) + 1, x(7), x(8) + 1, x(9)] """ if random_seed is not None: @@ -71,7 +71,7 @@ def sparse_random_system(ring, number_of_polynomials, def sparse_random_system_data_file_content( number_of_variables, **kwds): r""" - >>> sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) # doctest: +ELLIPSIS + sage: sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) # doctest: +ELLIPSIS "declare_ring(['x'+str(i) for in xrange(10)])\nideal=\\\n[...]\n\n" """ dummy_dict = dict() From ae30cf302ded1d18087bf156f535596555befdeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 19 Aug 2020 11:34:50 +1200 Subject: [PATCH 234/379] Basic conversion to sage doctrings/doctesting. Remove original copyright/license functions. --- src/sage/rings/polynomial/pbori/frontend.py | 77 ++++++++------------- 1 file changed, 28 insertions(+), 49 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/frontend.py b/src/sage/rings/polynomial/pbori/frontend.py index 6bf7ca0a1ec..ca41fc13c0c 100644 --- a/src/sage/rings/polynomial/pbori/frontend.py +++ b/src/sage/rings/polynomial/pbori/frontend.py @@ -3,29 +3,30 @@ This module defines an initial ring, and patches the declare_ring to use a given context. - -sage: x(0) -x(0) -sage: x(0)*x(0) -x(0) -sage: x(0) + x(0) -0 -sage: x(9999) -x(9999) -sage: x(9999)*x(9999) -x(9999) -sage: x(9999) + x(9999) -0 - -sage: from sage.rings.polynomial.pbori.brial.frontend import * -sage: context = dict(globals()) -sage: polybori_start(context) # doctest: +ELLIPSIS -ipbori... -sage: r = context['declare_ring']('abc') -sage: context['a'] -a -sage: r.variable(0) -a +EXAMPLES:: + + sage: x(0) + x(0) + sage: x(0)*x(0) + x(0) + sage: x(0) + x(0) + 0 + sage: x(9999) + x(9999) + sage: x(9999)*x(9999) + x(9999) + sage: x(9999) + x(9999) + 0 + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: context = dict(globals()) + sage: polybori_start(context) + ipbori... + sage: r = context['declare_ring']('abc') + sage: context['a'] + a + sage: r.variable(0) + a """ from __future__ import print_function @@ -36,7 +37,9 @@ def block_scheme_names(blocks): - """Helper for Singular interface.""" + r""" + Helper for Singular interface. + """ context = dict() from .blocks import declare_block_scheme @@ -47,29 +50,6 @@ def block_scheme_names(blocks): ipbname = 'ipbori' -def polybori_copyright(): - print("""Copyright (c) 2007-2011 by The PolyBoRi Team. - Michael Brickenstein (MFO) brickenstein@mfo.de - Alexander Dreyer (ITWM) alexander.dreyer@itwm.fraunhofer.de - -The PolyBoRi Team is a joint project of - Mathematisches Forschungsinstitut Oberwolfach (MFO), Germany - Department of Mathematics, University of Kaiserslautern, Germany, and - Fraunhofer Institute for Industrial Mathematics (ITWM), Kaiserslautern, Germany. - -PolyBoRi incorporates the following works: - The CU Decision Diagrams Package Release 2.4.1 (CUDD) by Fabio Somenzi, - Copyright (c) 1995-2004, Regents of the University of Colorado. All Rights Reserved. - The M4RI Library - http://m4ri.sagemath.org - Copyright (C) 2007-2010, Martin Albrecht, Gregory Bard, and The M4RI Team""") - - -def polybori_license(): - print("""ipbori and the PolyBoRi framework are licensed under the terms of -the GNU General Public License (GPL) version 2 or later. -See http://www.gnu.org/licenses/ for details.""") - - def polybori_start(global_context): def declare_ring(blocks, context=None): if context is None: @@ -79,8 +59,7 @@ def declare_ring(blocks, context=None): declare_ring.__doc__ = orig_declare_ring.__doc__ global_context["declare_ring"] = declare_ring - print(ipbname + """ -- The interactive command line tool of PolyBoRi %s -Type "polybori_copyright()" or "polybori_license()" for more information. + print(ipbname + """ -- The interactive command line tool of PolyBoRi/BRiAL %s """ % global_context.get("polybori_version", '')) # Here come the defaults From 4c0d80fa9266d46818e52b451b1d0aaada22f9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 19 Aug 2020 11:55:31 +1200 Subject: [PATCH 235/379] Some basic docstring in general_boolean_polynomial.py - remove memusage.py and its only import. --- .../pbori/general_boolean_polynomial.py | 80 +++++++++---------- src/sage/rings/polynomial/pbori/memusage.py | 65 --------------- 2 files changed, 40 insertions(+), 105 deletions(-) delete mode 100644 src/sage/rings/polynomial/pbori/memusage.py diff --git a/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py b/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py index 503406074d6..0595e86df16 100644 --- a/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py +++ b/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py @@ -1,10 +1,8 @@ -# -*- python -*- from __future__ import print_function import sys import resource from .gbcore import * -from .memusage import * from time import time, clock from .PyPolyBoRi import * from .blocks import declare_ring, Block @@ -27,17 +25,19 @@ def print_matrix(A): # TODO: Implement constructor to convert Polynomials to # GeneralBooleanPolynomial (with e_1 + ... + e_k as coefficient) class GeneralBooleanPolynomial: - """ + r""" Class to represent Boolean polynomials over F_2^k """ def __init__(self, k, coeff, polynomial): - """ + r""" Construct a GeneralBooleanPolynomial given by coeff * polynomial - Arguments: - k : Number of factors of F_2 - coeff : Array containing natural numbers in {0, ..., k-1} representing an element of F_2^k as a set - polynomial : Polynomial + + INPUT: + + - ``k`` -- Number of factors of F_2 + - ``coeff`` -- Array containing natural numbers in {0, ..., k-1} representing an element of F_2^k as a set + - ``polynomial`` -- Polynomial """ #print "type of polynomials", type(polynomial) #print "polynomials is int?", isinstance(polynomial, int) @@ -54,28 +54,28 @@ def __init__(self, k, coeff, polynomial): self.polys.append(Polynomial(0, self.ring)) def __len__(self): - """ + r""" Returns the number of factors k in the product of the underlying ring F_2^k """ return self.k def __getitem__(self, k): - """ + r""" Return the k-th component (i.e. the projection to the k-th factor) """ return self.polys[k] def __setitem__(self, k, value): - """ + r""" Sets the k-th component (i.e. the projection to the k-th factor) """ self.polys[k] = value def __eq__(self, other): - """ + r""" Tests equality by testing that - - both objects are defined over the same ring (i.e. the number of factors is the same) - - the objects are equal in each component + - both objects are defined over the same ring (i.e. the number of factors is the same) + - the objects are equal in each component """ if not len(self) == len(other): return False @@ -88,7 +88,7 @@ def __ne__(self, other): return not self == other def __str__(self): - """ + r""" Returns a representation of the polynomial as string """ res = "" @@ -119,7 +119,7 @@ def __str__(self): return res def __add__(self, other): - """ + r""" Addition of two GeneralBooleanPolynomial """ if not len(self) == len(other): @@ -133,7 +133,7 @@ def __add__(self, other): return sum def __mul__(self, other): - """ + r""" Multiplication of two GeneralBooleanPolynomial """ if not len(self) == len(other): @@ -147,7 +147,7 @@ def __mul__(self, other): return prod def __sub__(self, other): - """ + r""" Subtraction of two GeneralBooleanPolynomial """ if not len(self) == len(other): @@ -161,14 +161,14 @@ def __sub__(self, other): return sub def lc(self): - """ + r""" Returns leading coefficient as constant GeneralBooleanPolynomial """ return GeneralBooleanPolynomial(self.k, self.lc_as_set(), Polynomial(1, self.ring)) def lc_as_set_array(self): - """ + r""" Returns leading coefficient as array containing the indices of the non-zero components of the leading coefficient. """ @@ -182,14 +182,14 @@ def lc_as_set_array(self): return comps def lc_as_set(self): - """ + r""" Returns leading coefficient as set containing the indices of the non-zero components of the leading coefficient. """ return set(self.lc_as_set_array()) def lc_binary(self): - """ + r""" Returns leading coefficient as array containing the integers 0 or 1 representing the leading coefficient in a binary form. """ @@ -201,7 +201,7 @@ def lc_binary(self): return lc_binary def lt(self): - """ + r""" Leading term in form of a GeneralBooleanPolynomial """ max_term = 1 @@ -213,7 +213,7 @@ def lt(self): return GeneralBooleanPolynomial(self.k, comps, max_term) def lm(self): - """ + r""" Leading monomial in form of a GeneralBooleanPolynomial """ max_term = 1 @@ -229,7 +229,7 @@ def lm(self): max_term) def constant_part_binary(self): - """ + r""" Constant part as binary tuple indicading which coefficients are non-zero """ comps = [] @@ -241,7 +241,7 @@ def constant_part_binary(self): return comps def constant_part_as_set_array(self): - """ + r""" Constant part as array containing the indices of the non-zero coefficients of the constant part (sorted increasingly) """ res = [] @@ -251,13 +251,13 @@ def constant_part_as_set_array(self): return res def constant_part_as_set(self): - """ + r""" Constant part as set containing the indices of the non-zero coefficients of the constant part """ return set(self.constant_part_as_set_array()) def constant_part(self): - """ + r""" Constant part as GeneralBoolenPolynomial """ res = GeneralBooleanPolynomial(len(self), [], Polynomial(0, self.ring)) @@ -269,7 +269,7 @@ def constant_part(self): return res def to_expanded_polynomial_ring(self, new_variables): - """ + r""" Returns a representation in form of a Polynomial over a ring with additional variables, one for each factor in the product of fields F_2^k @@ -278,7 +278,7 @@ def to_expanded_polynomial_ring(self, new_variables): return sum(new_variables[i] * self.polys[i] for i in range(self.k)) def is_monomial(self): - """ + r""" Test if self is a Monomial """ # Build a list of all terms occurring @@ -295,7 +295,7 @@ def is_monomial(self): return True def is_zero(self): - """ + r""" Tests if self is zero """ for i in range(len(self)): @@ -304,7 +304,7 @@ def is_zero(self): return True def monomial(self): - """ + r""" Returns a PolyBoRi Monomial representing the leading monomial of self, where self should be a monomial """ assert self.is_monomial() @@ -314,7 +314,7 @@ def monomial(self): return Polynomial(0, self.ring) def divides(self, other): - """ + r""" Tests if self divides other """ assert len(self) == len(other) @@ -374,7 +374,7 @@ def triangulate_over_F2(A, b): def projection_of_expanded_polynomial(f, e, e_vars): - """ + r""" Compute the projection of the expanded polynomial f to the component corresponding to the variable e (which is part of e_vars) """ @@ -390,7 +390,7 @@ def projection_of_expanded_polynomial(f, e, e_vars): def expanded_polynomial2general_polynomial(polynomial, new_variables, ring): - """ + r""" Returns a GeneralBooleanPolynomial associated to a Polynomial (polynomial) in additional variables (new_variables), where the GeneralBooleanPolynomial in obtained from the projections of polynomial @@ -406,7 +406,7 @@ def expanded_polynomial2general_polynomial(polynomial, new_variables, ring): def reduce_general_boolean_polynomial(F, polynomial): - """ + r""" Computes the reduction of polynomial via the ideal given by F """ r = polynomial @@ -479,7 +479,7 @@ def included(i, set): def stratified(I): - """ + r""" Tests if I does no contain two polynomials with the same leading monomials """ leading_monomials = [] @@ -501,7 +501,7 @@ def build_dict_from_array_of_extended_polynomials(A, e_vars): def stratify_dict_I_gb_I(dict, e_vars, debug=0): - """ + r""" Wrapper (calls either stratify_dict_I_gb_I_our_alg or stratify_dict_I_gb_I_Inoue """ #return stratify_dict_I_gb_I_Inoue(dict, e_vars, debug) @@ -509,7 +509,7 @@ def stratify_dict_I_gb_I(dict, e_vars, debug=0): def stratify_dict_I_gb_I_our_alg(dict, e_vars, debug=0): - """ + r""" Build a stratified Groebner bases for dict """ # Ideal for the polynomials of the new basis @@ -586,7 +586,7 @@ def stratify_dict_I_gb_I_our_alg(dict, e_vars, debug=0): def stratify_dict_I_gb_I_Inoue(dict, e_vars, debug=0): - """ + r""" Reimplementation of a simple algorithm of Inoue from BGSet """ # Ideal for the polynomials of the new basis diff --git a/src/sage/rings/polynomial/pbori/memusage.py b/src/sage/rings/polynomial/pbori/memusage.py deleted file mode 100644 index 521442c83bb..00000000000 --- a/src/sage/rings/polynomial/pbori/memusage.py +++ /dev/null @@ -1,65 +0,0 @@ -import os - -_proc_status = '/proc/%d/status' % os.getpid() -#_scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, -# 'KB': 1024.0, 'MB': 1024.0*1024.0} - -_scale = {'kB': 1, 'mB': 1024, 'gB': 1024 * 1024, - 'KB': 1, 'MB': 1024, 'GB': 1024 * 1024} - - -def _VmB(VmKey): - '''Private. - ''' - global _proc_status, _scale - # get pseudo file /proc//status - try: - t = open(_proc_status) - v = t.read() - t.close() - except: - return float('nan') # non-Linux? - # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' - i = v.index(VmKey) - v = v[i:].split(None, 3) # whitespace - if len(v) < 3: - return float('nan') # invalid format? - # convert Vm value to bytes - # return float(v[1]) * _scale[v[2]] - return int(v[1]) * _scale[v[2]] - - -def memory(since=0): - '''Return memory usage in kilobytes. - ''' - return _VmB('VmSize:') - since - - -def resident(since=0): - '''Return resident memory usage in kilobytes. - ''' - return _VmB('VmRSS:') - since - - -def memorypeak(since=0): - '''Return memory usage peak in kilobytes. - ''' - try: - return _VmB('VmPeak:') - since - except: - return float('nan') # old Linux? - - -def residentpeak(since=0): - '''Return resident memory usage peak in kilobytes. - ''' - try: - return _VmB('VmHWM:') - since - except: - return float('nan') # old Linux? - - -def stacksize(since=0): - '''Return stack size in kilobytes. - ''' - return _VmB('VmStk:') - since From bdf1fecd4a5532f09abf6cb204dfeb705dfd253e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 19 Aug 2020 12:19:43 +1200 Subject: [PATCH 236/379] First pass in converting to docstring and doctests. It is quite likely a lot of doctests are broken at this stage. --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 117 +++++++------- src/sage/rings/polynomial/pbori/addition.py | 115 ++++++++------ src/sage/rings/polynomial/pbori/blocks.py | 43 +++--- .../rings/polynomial/pbori/check_claims.py | 6 +- src/sage/rings/polynomial/pbori/cluster.py | 5 +- src/sage/rings/polynomial/pbori/cnf.py | 144 ++++++++++-------- src/sage/rings/polynomial/pbori/context.py | 70 +++++---- .../polynomial/pbori/easy_polynomials.py | 39 ++--- src/sage/rings/polynomial/pbori/fglm.py | 84 +++++----- src/sage/rings/polynomial/pbori/interred.py | 2 +- src/sage/rings/polynomial/pbori/intersect.py | 31 ++-- src/sage/rings/polynomial/pbori/intpolys.py | 29 ++-- src/sage/rings/polynomial/pbori/ll.py | 96 ++++++------ src/sage/rings/polynomial/pbori/nf.py | 45 ++++-- src/sage/rings/polynomial/pbori/parallel.py | 88 ++++++----- src/sage/rings/polynomial/pbori/parsegat.py | 24 ++- src/sage/rings/polynomial/pbori/partial.py | 4 +- src/sage/rings/polynomial/pbori/plot.py | 29 ++-- src/sage/rings/polynomial/pbori/randompoly.py | 32 ++-- src/sage/rings/polynomial/pbori/simplebb.py | 14 +- 20 files changed, 584 insertions(+), 433 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index d43832d4e1e..40420c5620a 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -1,66 +1,67 @@ -"""PolyBoRi's interface to libpolybori* +r""" +PolyBoRi's interface to libpolybori/BRiAL This file makes interfaces to PolyBoRi's runtime libraries available in Python via sage. AUTHOR: - The PolyBoRi Team, 2007-2012 - - Examples: - - sage: from sage.rings.polynomial.pbori.brial.frontend import * - sage: r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) - sage: x0>x1 - True - sage: x0>x1*x2 - True - sage: y0>y1 - True - sage: y0>y1*y2 - True - - sage: r = r.clone(ordering=dlex) - sage: r(x0) > r(x1) - True - sage: r(x0) > r(x1*x2) - False - - sage: r = r.clone(ordering=dp_asc) - sage: r(x0) > r(x1) - False - sage: r(x0) > r(x1*x2) - False - - sage: r = r.clone(ordering=block_dlex, blocks=[3]) - sage: r(x0) > r(x1) - True - sage: r(x0) > r(x1*x2) - False - sage: r(x0) > r(y0*y1*y2) - True - - sage: r = r.clone(ordering=block_dp_asc) - sage: r(x0) > r(x1) - False - sage: r(x0) > r(y0) - False - sage: r(x0) > r(x1*x2) - False - - sage: r = r.clone(ordering=block_dp_asc, blocks=[3]) - sage: r(x0) > r(y0) - True - - sage: r(x0) > r(y0*y1) - True - - sage: r = r.clone(names=["z17", "z7"]) - sage: [r.variable(idx) for idx in xrange(3)] - [z17, z7, x2] - sage: r = r.clone(names="abcde") - sage: [r.variable(idx) for idx in xrange(6)] - [a, b, c, d, e, y2] +- The PolyBoRi Team, 2007-2012 + + EXAMPLES:: + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) + sage: x0>x1 + True + sage: x0>x1*x2 + True + sage: y0>y1 + True + sage: y0>y1*y2 + True + + sage: r = r.clone(ordering=dlex) + sage: r(x0) > r(x1) + True + sage: r(x0) > r(x1*x2) + False + + sage: r = r.clone(ordering=dp_asc) + sage: r(x0) > r(x1) + False + sage: r(x0) > r(x1*x2) + False + + sage: r = r.clone(ordering=block_dlex, blocks=[3]) + sage: r(x0) > r(x1) + True + sage: r(x0) > r(x1*x2) + False + sage: r(x0) > r(y0*y1*y2) + True + + sage: r = r.clone(ordering=block_dp_asc) + sage: r(x0) > r(x1) + False + sage: r(x0) > r(y0) + False + sage: r(x0) > r(x1*x2) + False + + sage: r = r.clone(ordering=block_dp_asc, blocks=[3]) + sage: r(x0) > r(y0) + True + + sage: r(x0) > r(y0*y1) + True + + sage: r = r.clone(names=["z17", "z7"]) + sage: [r.variable(idx) for idx in xrange(3)] + [z17, z7, x2] + sage: r = r.clone(names="abcde") + sage: [r.variable(idx) for idx in xrange(6)] + [a, b, c, d, e, y2] """ from sage import all @@ -97,7 +98,7 @@ def WeakRingRef(ring): def add_up_polynomials(polys, init): - """ + r""" Adds up the polynomials in polys (which should be a BoolePolynomialVector or a sequence of ??? """ if not isinstance(polys, BoolePolynomialVector): diff --git a/src/sage/rings/polynomial/pbori/addition.py b/src/sage/rings/polynomial/pbori/addition.py index 54a61408c35..ae7318c5262 100644 --- a/src/sage/rings/polynomial/pbori/addition.py +++ b/src/sage/rings/polynomial/pbori/addition.py @@ -5,13 +5,17 @@ def add_bits_old(bits): - """Adds n bits - sage: from sage.rings.polynomial.pbori.brial import * - sage: r=Ring(10) - sage: add_bits_old([r.variable(i) for i in xrange(3)]) - [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] - sage: add_bits_old([r.variable(i) for i in xrange(4)]) - [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3)] + r""" + Adds n bits + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(10) + sage: add_bits_old([r.variable(i) for i in xrange(3)]) + [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] + sage: add_bits_old([r.variable(i) for i in xrange(4)]) + [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3)] """ bits = list(bits) n = len(bits) @@ -32,15 +36,19 @@ def add_bits_old(bits): def add_bits(bits): - """Adds n bit variables, by Lucas theorem - sage: from sage.rings.polynomial.pbori.brial import * - sage: r=Ring(10) - sage: add_bits([r.variable(i) for i in xrange(3)]) - [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] - sage: add_bits([r.variable(i) for i in xrange(4)]) - [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3), x(0)*x(1)*x(2)*x(3)] - sage: add_bits([r.variable(0)]) - [x(0)] + r""" + Adds n bit variables, by Lucas theorem + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(10) + sage: add_bits([r.variable(i) for i in xrange(3)]) + [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] + sage: add_bits([r.variable(i) for i in xrange(4)]) + [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3), x(0)*x(1)*x(2)*x(3)] + sage: add_bits([r.variable(0)]) + [x(0)] """ bits = list(bits) if len(bits) < 2: @@ -57,18 +65,21 @@ def add_bits(bits): def add_bit_expressions(bit_expressions): - """Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. - - sage: from sage.rings.polynomial.pbori.brial import * - sage: r=Ring(20) - sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) - [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] - sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) - [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] - sage: add_bit_expressions([r.variable(11), r.variable(11)]) - [0, x(11)] - sage: add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) - [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] + r""" + Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(20) + sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) + [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] + sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) + [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] + sage: add_bit_expressions([r.variable(11), r.variable(11)]) + [0, x(11)] + sage: add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) + [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] """ bit_variables = [] @@ -84,18 +95,22 @@ def add_bit_expressions(bit_expressions): def add_words(words): - """def adds n words, this words are supposed to consists of list of their bits. - sage: from sage.rings.polynomial.pbori.brial import * - sage: r=Ring(1000) - sage: add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) - [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] - sage: res=add_words([[r.variable(100+i*9+j) for i in xrange(4)] for j in xrange(9)]) - sage: [len(p) for p in res] - [9, 45, 495, 12870, 735462, 70285482, 1891358892, 6435] - sage: [p.deg() for p in res] - [1, 2, 4, 8, 12, 18, 25, 33] - sage: [p.n_nodes() for p in res] - [9, 25, 54, 100, 153, 211, 249, 100] + r""" + Adds n words, this words are supposed to consists of list of their bits. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(1000) + sage: add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) + [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] + sage: res=add_words([[r.variable(100+i*9+j) for i in xrange(4)] for j in xrange(9)]) + sage: [len(p) for p in res] + [9, 45, 495, 12870, 735462, 70285482, 1891358892, 6435] + sage: [p.deg() for p in res] + [1, 2, 4, 8, 12, 18, 25, 33] + sage: [p.n_nodes() for p in res] + [9, 25, 54, 100, 153, 211, 249, 100] """ max_word_length = max((len(w) for w in words)) @@ -111,14 +126,18 @@ def add_words(words): def multiply_by_addition(word_a, word_b): - """Multiply two words - sage: from sage.rings.polynomial.pbori.brial import Ring - sage: r=Ring(1000) - sage: x = r.variable - sage: n=7 - sage: res=multiply_by_addition([x(200+2*i) for i in xrange(n)], [x(200+2*i+1) for i in xrange(n)]) - sage: [p.n_nodes() for p in res] - [2, 4, 7, 17, 38, 85, 222, 630, 1358, 1702, 1713, 1430, 875, 214, 0] + r""" + Multiply two words + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import Ring + sage: r=Ring(1000) + sage: x = r.variable + sage: n=7 + sage: res=multiply_by_addition([x(200+2*i) for i in xrange(n)], [x(200+2*i+1) for i in xrange(n)]) + sage: [p.n_nodes() for p in res] + [2, 4, 7, 17, 38, 85, 222, 630, 1358, 1702, 1713, 1430, 875, 214, 0] """ word_a = list(word_a) word_b = list(word_b) diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py index 2152c33b35b..a2fb886b956 100644 --- a/src/sage/rings/polynomial/pbori/blocks.py +++ b/src/sage/rings/polynomial/pbori/blocks.py @@ -15,10 +15,11 @@ class Block(object): - """The block class represents a block of variables - (start_index,...,start_index+size-1), it is the preferred - block type for simple one-dimensional variable sets""" - + r""" + The block class represents a block of variables + (start_index,...,start_index+size-1), it is the preferred + block type for simple one-dimensional variable sets + """ def __init__(self, var_name, size, start_index=0, reverse=False): indices = range(start_index, start_index + size) if reverse: @@ -54,10 +55,11 @@ def register(self, start, context): class AlternatingBlock(object): - """The Alternating Block class is used for doing tricky variable - schemes,where base names vary, e.g. - a(0),b(0),a(1),b(1),a(2),b(2)""" - + r""" + The Alternating Block class is used for doing tricky variable + schemes,where base names vary, e.g. + a(0),b(0),a(1),b(1),a(2),b(2) + """ def __init__(self, var_names, size_per_variable, start_index=0, reverse=False): self.var_names = var_names @@ -161,12 +163,12 @@ def implement(self, equations): class HigherOrderBlock(object): - """HigherOrderBlocks are multidimensional blocks of variables, for each dimension a seperate start_index and size can be specified + r""" + HigherOrderBlocks are multidimensional blocks of variables, for each dimension a seperate start_index and size can be specified var_name : variables will be called (multiindex), where multiindex is a tuple of the size size_tuple : specifies the sizes of the ranges of each component of the multi-indices start_index_tuple : the multi-indices will be of the form start_index_tuple + a, where a is a multi-index with non-negative components - """ def __init__(self, var_name, size_tuple, start_index_tuple=None, @@ -358,14 +360,19 @@ def if_then(i, t, supposed_to_be_valid=True): def declare_ring(blocks, context=None): - """Declare Ring is the preferred function to create a ring and declare a variable scheme, - the number of variables is automatically determined, - usually you pass globals() as context argument to store the ring and the variable mapping. - Example - declare_ring([Block("x",10),Block("y",5)],globals()) - gives a ring with x(0..9),y(0..4) and registers the ring as r, - and the variable blocks x and y in the context dictionary globals(), which consists of the global variables of the python module - """ + r""" + Declare Ring is the preferred function to create a ring and declare a variable scheme, + the number of variables is automatically determined, usually you pass globals() as context + argument to store the ring and the variable mapping. + + EXAMPLES:: + + sage: declare_ring([Block("x",10),Block("y",5)],globals()) + + gives a ring with x(0..9),y(0..4) and registers the ring as r, and the variable + blocks x and y in the context dictionary globals(), which consists of the global + variables of the python module + """ if context is None: context = sys.modules['__main__'].__dict__ diff --git a/src/sage/rings/polynomial/pbori/check_claims.py b/src/sage/rings/polynomial/pbori/check_claims.py index 66529ab0c85..afa11b9b0e5 100644 --- a/src/sage/rings/polynomial/pbori/check_claims.py +++ b/src/sage/rings/polynomial/pbori/check_claims.py @@ -1,9 +1,9 @@ # encoding: utf-8 -""" -untitled.py +r""" +check_claims.py Created by Michael Brickenstein on 2007-03-05. -Copyright (c) 2007 The PolyBoRi Team. See LICENSE file. +Copyright (c) 2007 The PolyBoRi Team. """ from __future__ import print_function diff --git a/src/sage/rings/polynomial/pbori/cluster.py b/src/sage/rings/polynomial/pbori/cluster.py index 036f04dc42d..5d35766b3b2 100644 --- a/src/sage/rings/polynomial/pbori/cluster.py +++ b/src/sage/rings/polynomial/pbori/cluster.py @@ -1,10 +1,9 @@ -# -*- python -*- # encoding: utf-8 -""" +r""" cluster.py Created by Michael Brickenstein on 2011-08-05. -Copyright 2011 The PolyBoRi Team. See LICENSE file. +Copyright 2011 The PolyBoRi Team. """ import sys diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py index c3fd41c068f..8b5eb453e0e 100644 --- a/src/sage/rings/polynomial/pbori/cnf.py +++ b/src/sage/rings/polynomial/pbori/cnf.py @@ -13,12 +13,16 @@ def __init__(self, r, random_seed=16): self.r = r def zero_blocks(self, f): - """divides the zero set of f into blocks - sage: from sage.rings.polynomial.pbori.brial import * - sage: r = declare_ring(["x", "y", "z"], dict()) - sage: e = CNFEncoder(r) - sage: e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) - [{y: 0}, {z: 0}, {x: 0}] + r""" + Divides the zero set of f into blocks + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) + [{y: 0}, {z: 0}, {x: 0}] """ f = Polynomial(f) variables = f.vars_as_monomial() @@ -77,19 +81,22 @@ def get_val(var): return res def clauses(self, f): - """ - sage: from sage.rings.polynomial.pbori.brial import * - sage: r = declare_ring(["x", "y", "z"], dict()) - sage: e = CNFEncoder(r) - sage: e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS - [{...x: 0...}] - sage: e.clauses(r.variable(1)+r.variable(0)) # doctest:+ELLIPSIS - [{...x: 1...}, {...y: 1...}] - - sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(0)*r.variable(1)*r.variable(2))] - [[(z, 0), (y, 0), (x, 0)]] - sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(1)+r.variable(0))] - [[(y, 1), (x, 0)], [(y, 0), (x, 1)]] + r""" + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS + [{...x: 0...}] + sage: e.clauses(r.variable(1)+r.variable(0)) # doctest:+ELLIPSIS + [{...x: 1...}, {...y: 1...}] + + sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(0)*r.variable(1)*r.variable(2))] + [[(z, 0), (y, 0), (x, 0)]] + sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(1)+r.variable(0))] + [[(y, 1), (x, 0)], [(y, 0), (x, 1)]] """ f_plus_one = f + 1 blocks = self.zero_blocks(f + 1) @@ -101,16 +108,19 @@ def clauses(self, f): return negated_blocks def polynomial_clauses(self, f): - """ - sage: from sage.rings.polynomial.pbori.brial import * - sage: r = declare_ring(["x", "y", "z"], dict()) - sage: e = CNFEncoder(r) - sage: e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) - [x*y*z] - sage: v = r.variable - sage: p = v(1)*v(2)+v(2)*v(0)+1 - sage: groebner_basis([p], heuristic = False)==groebner_basis(e.polynomial_clauses(p), heuristic = False) - True + r""" + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) + [x*y*z] + sage: v = r.variable + sage: p = v(1)*v(2)+v(2)*v(0)+1 + sage: groebner_basis([p], heuristic = False)==groebner_basis(e.polynomial_clauses(p), heuristic = False) + True """ def product(l): @@ -140,14 +150,17 @@ def get_sign(val): for (variable, value) in items] + [0]]) def dimacs_encode_polynomial(self, p): - """ - sage: from sage.rings.polynomial.pbori.brial import * - sage: d=dict() - sage: r = declare_ring(["x", "y", "z"], d) - sage: e = CNFEncoder(r) - sage: e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]) - ['1 2 -3 0', '1 -2 3 0', '-1 -2 -3 0', '-1 2 3 0'] - """ + r""" + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: d=dict() + sage: r = declare_ring(["x", "y", "z"], d) + sage: e = CNFEncoder(r) + sage: e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]) + ['1 2 -3 0', '1 -2 3 0', '-1 -2 -3 0', '-1 2 3 0'] + """ clauses = self.clauses(p) res = [] for c in clauses: @@ -156,15 +169,18 @@ def dimacs_encode_polynomial(self, p): def dimacs_cnf(self, polynomial_system): r""" - sage: from sage.rings.polynomial.pbori.brial import * - sage: r = declare_ring(["x", "y", "z"], dict()) - sage: e = CNFEncoder(r) - sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) - 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0' - sage: e.dimacs_cnf([r.variable(1)+r.variable(0)]) - 'c cnf generated by PolyBoRi\np cnf 3 2\n1 -2 0\n-1 2 0' - sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) - 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n-1 2 0\n1 -2 0' + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r = declare_ring(["x", "y", "z"], dict()) + sage: e = CNFEncoder(r) + sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) + 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0' + sage: e.dimacs_cnf([r.variable(1)+r.variable(0)]) + 'c cnf generated by PolyBoRi\np cnf 3 2\n1 -2 0\n-1 2 0' + sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) + 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n-1 2 0\n1 -2 0' """ clauses_list = [c for p in polynomial_system for c in self. dimacs_encode_polynomial(p)] @@ -182,20 +198,23 @@ class CryptoMiniSatEncoder(CNFEncoder): def dimacs_encode_polynomial(self, p): r""" - sage: from sage.rings.polynomial.pbori.brial import * - sage: d=dict() - sage: r = declare_ring(["x", "y", "z"], d) - sage: e = CryptoMiniSatEncoder(r) - sage: p = d["x"]+d["y"]+d["z"] - sage: p.deg() - 1 - sage: len(p) - 3 - sage: e.dimacs_encode_polynomial(p) - ['x1 2 3 0\nc g 1 x + y + z'] - sage: e.dimacs_encode_polynomial(p+1) - ['x1 2 -3 0\nc g 2 x + y + z + 1'] - """ + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: d=dict() + sage: r = declare_ring(["x", "y", "z"], d) + sage: e = CryptoMiniSatEncoder(r) + sage: p = d["x"]+d["y"]+d["z"] + sage: p.deg() + 1 + sage: len(p) + 3 + sage: e.dimacs_encode_polynomial(p) + ['x1 2 3 0\nc g 1 x + y + z'] + sage: e.dimacs_encode_polynomial(p+1) + ['x1 2 -3 0\nc g 2 x + y + z + 1'] + """ if p.deg() != 1 or len(p) <= 1: res = super(CryptoMiniSatEncoder, self).dimacs_encode_polynomial(p) else: @@ -216,6 +235,9 @@ def dimacs_encode_polynomial(self, p): def dimacs_cnf(self, polynomial_system): r""" + + TESTS:: + sage: from sage.rings.polynomial.pbori.brial import * sage: r = declare_ring(["x", "y", "z"], dict()) sage: e = CryptoMiniSatEncoder(r) @@ -225,7 +247,7 @@ def dimacs_cnf(self, polynomial_system): 'c cnf generated by PolyBoRi\np cnf 3 1\nx1 2 0\nc g 2 x + y\nc v 1 x\nc v 2 y' sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) 'c cnf generated by PolyBoRi\np cnf 3 2\n-1 -2 -3 0\nc g 3 x*y*z\nx1 2 0\nc g 4 x + y\nc v 1 x\nc v 2 y\nc v 3 z' - """ + """ uv = list(used_vars_set(polynomial_system).variables()) res = super(CryptoMiniSatEncoder, self).dimacs_cnf(polynomial_system) res = res + "\n" + "\n".join(["c v %s %s" % (self.to_dimacs_index(v), diff --git a/src/sage/rings/polynomial/pbori/context.py b/src/sage/rings/polynomial/pbori/context.py index 4c13f59ac73..323d2475682 100644 --- a/src/sage/rings/polynomial/pbori/context.py +++ b/src/sage/rings/polynomial/pbori/context.py @@ -5,10 +5,14 @@ def _exists(): - """PolyBoRi convention: checking optional components for prerequisites here - - sage: _exists() - True + r""" + PolyBoRi convention: checking optional components for prerequisites here + + TESTS:: + + sage: from sage.rings.polynomial.pbori.context import * + sage: _exists() + True """ from distutils.sysconfig import get_python_version return float(get_python_version()) > 2.4 @@ -20,21 +24,23 @@ def _exists(): class FactoryContext(object): - """Temporarily exchange the constructor of a given type with a compatible + r""" + Temporarily exchange the constructor of a given type with a compatible callable object. It is useful together with the with statement. - Example: - sage: r = Ring(1000) - sage: from sage.rings.polynomial.pbori.brial import Variable - sage: def var(idx): return Variable(idx, r) - sage: with FactoryContext(Variable, var): - ... print Variable(17) - x(17) - sage: try: - ... print Variable(17) - ... except: - ... print "caught expected exception" - caught expected exception + EXAMPLES:: + + sage: r = Ring(1000) + sage: from sage.rings.polynomial.pbori.brial import Variable + sage: def var(idx): return Variable(idx, r) + sage: with FactoryContext(Variable, var): + ... print Variable(17) + x(17) + sage: try: + ... print Variable(17) + ... except: + ... print "caught expected exception" + caught expected exception """ def __init__(self, original, factory): @@ -59,23 +65,25 @@ def __exit__(self, type, value, traceback): class RingContext(object): - """Temporarily fix the ring for constructors of some ring-dependent types + r""" + Temporarily fix the ring for constructors of some ring-dependent types like Variable and Monomial to a given ring. It is useful together with the with statement. - Example: - sage: r = Ring(1000) - sage: from sage.rings.polynomial.pbori.brial import Variable - sage: print Variable(17, r) - x(17) - sage: with RingContext(r): - ... print Variable(17), Monomial(), Polynomial(0), BooleSet() - x(17) 1 0 {} - sage: try: - ... print Variable(17) - ... except: - ... print "caught expected exception" - caught expected exception + EXAMPLES:: + + sage: r = Ring(1000) + sage: from sage.rings.polynomial.pbori.brial import Variable + sage: print Variable(17, r) + x(17) + sage: with RingContext(r): + ... print Variable(17), Monomial(), Polynomial(0), BooleSet() + x(17) 1 0 {} + sage: try: + ... print Variable(17) + ... except: + ... print "caught expected exception" + caught expected exception """ def __init__(self, ring): diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py index c788bb77602..64ca30ef7a2 100644 --- a/src/sage/rings/polynomial/pbori/easy_polynomials.py +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -3,15 +3,18 @@ def easy_linear_polynomials(p): - """ Get linear polynomials implied by given polynomial. + r""" + Get linear polynomials implied by given polynomial. - sage: from sage.rings.polynomial.pbori.brial.frontend import * - sage: easy_linear_polynomials(x(1)*x(2) + 1) - [x(1) + 1, x(2) + 1] - sage: easy_linear_polynomials(x(1)*x(2) + 0) - [] - sage: easy_linear_polynomials(x(0)*x(1) + x(0)*x(2) + 1) - [x(0) + 1, x(1) + x(2) + 1] + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: easy_linear_polynomials(x(1)*x(2) + 1) + [x(1) + 1, x(2) + 1] + sage: easy_linear_polynomials(x(1)*x(2) + 0) + [] + sage: easy_linear_polynomials(x(0)*x(1) + x(0)*x(2) + 1) + [x(0) + 1, x(1) + x(2) + 1] """ res = [] if p.deg() >= 2: @@ -25,16 +28,18 @@ def easy_linear_polynomials(p): def easy_linear_polynomials_via_interpolation(p): - """ Get linear polynomials implied by given polynomial using interpolation - of the variety. + r""" + Get linear polynomials implied by given polynomial using interpolation of the variety. - sage: from sage.rings.polynomial.pbori.brial.frontend import * - sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) - [x(1) + 1, x(2) + 1] - sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) - [] - sage: easy_linear_polynomials_via_interpolation(x(0)*x(1) + x(0)*x(2) + 1) - [x(0) + 1, x(1) + x(2) + 1] + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) + [x(1) + 1, x(2) + 1] + sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) + [] + sage: easy_linear_polynomials_via_interpolation(x(0)*x(1) + x(0)*x(2) + 1) + [x(0) + 1, x(1) + x(2) + 1] """ res = [] p_vars = p.vars_as_monomial() diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index 7fb85daab71..f29bbc65d5c 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -15,25 +15,30 @@ def _test(): def _fglm(I, from_ring, to_ring): - """Unchecked variant of fglm""" + r""" + Unchecked variant of fglm + """ vec = BoolePolynomialVector(I) return FGLMStrategy(from_ring, to_ring, vec).main() def fglm(I, from_ring, to_ring): - """ - converts *reduced* Groebner Basis in from_ring to a GroebnerBasis in to_ring. + r""" + Converts *reduced* Groebner Basis in from_ring to a GroebnerBasis in to_ring. It acts independend of the global ring, which is restored at the end of the - computation, - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode - sage: dp_asc = OrderCode.dp_asc - sage: r=declare_ring(['x','y','z'],dict()) - sage: old_ring = r - sage: new_ring = old_ring.clone(ordering=dp_asc) - sage: (x,y,z) = [old_ring.variable(i) for i in xrange(3)] - sage: ideal=[x+z, y+z]# lp Groebner basis - sage: list(fglm(ideal, old_ring, new_ring)) - [y + x, z + x] + computation. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: dp_asc = OrderCode.dp_asc + sage: r=declare_ring(['x','y','z'],dict()) + sage: old_ring = r + sage: new_ring = old_ring.clone(ordering=dp_asc) + sage: (x,y,z) = [old_ring.variable(i) for i in xrange(3)] + sage: ideal=[x+z, y+z]# lp Groebner basis + sage: list(fglm(ideal, old_ring, new_ring)) + [y + x, z + x] """ for poly in I: if poly.ring().id() != from_ring.id(): @@ -42,35 +47,42 @@ def fglm(I, from_ring, to_ring): def vars_real_divisors(monomial, monomial_set): - """ - returns all elements of of monomial_set, which result multiplied by a variable in monomial. - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode - sage: dp_asc = OrderCode.dp_asc - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring - sage: r=Ring(1000) - sage: x = r.variable - sage: b=BooleSet([x(1)*x(2),x(2)]) - sage: vars_real_divisors(x(1)*x(2)*x(3),b) - {{x(1),x(2)}} + r""" + Returns all elements of of monomial_set, which result multiplied by a variable in monomial. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: dp_asc = OrderCode.dp_asc + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: r=Ring(1000) + sage: x = r.variable + sage: b=BooleSet([x(1)*x(2),x(2)]) + sage: vars_real_divisors(x(1)*x(2)*x(3),b) + {{x(1),x(2)}} """ return BooleSet(Polynomial(monomial_set.divisors_of(monomial)). \ graded_part(monomial.deg() - 1)) def m_k_plus_one(completed_elements, variables): - """ calculates $m_{k+1}$ from the FGLM algorithm as described in Wichmanns diploma thesis - It would be nice to be able to efficiently extract the smallest term of a polynomial - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode - sage: dp_asc = OrderCode.dp_asc - sage: r=Ring(1000) - sage: x = r.variable - sage: s=BooleSet([x(1)*x(2),x(1),x(2),Monomial(r),x(3)]) - sage: variables=BooleSet([x(1),x(2),x(3)]) - sage: m_k_plus_one(s,variables) - x(2)*x(3) - sage: r2 = r.clone(ordering=dp_asc) - sage: m_k_plus_one(r2(s).set(),r2(variables).set()) - x(1)*x(3) + r""" + Calculates $m_{k+1}$ from the FGLM algorithm as described in Wichmanns diploma thesis + It would be nice to be able to efficiently extract the smallest term of a polynomial. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: dp_asc = OrderCode.dp_asc + sage: r=Ring(1000) + sage: x = r.variable + sage: s=BooleSet([x(1)*x(2),x(1),x(2),Monomial(r),x(3)]) + sage: variables=BooleSet([x(1),x(2),x(3)]) + sage: m_k_plus_one(s,variables) + x(2)*x(3) + sage: r2 = r.clone(ordering=dp_asc) + sage: m_k_plus_one(r2(s).set(),r2(variables).set()) + x(1)*x(3) """ return sorted(completed_elements.cartesian_product(variables).diff( completed_elements))[0] diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py index f7f03e7d612..c6634bafc23 100644 --- a/src/sage/rings/polynomial/pbori/interred.py +++ b/src/sage/rings/polynomial/pbori/interred.py @@ -2,7 +2,7 @@ def interred(l, completely=False): - """computes a new generating system (g1, ...,gn), + r"""computes a new generating system (g1, ...,gn), spanning the same ideal modulo field equations. The system is interreduced: For i!=j: gi.lead() does not divide any leading term of gj. diff --git a/src/sage/rings/polynomial/pbori/intersect.py b/src/sage/rings/polynomial/pbori/intersect.py index c40c8462545..c7b1405375f 100644 --- a/src/sage/rings/polynomial/pbori/intersect.py +++ b/src/sage/rings/polynomial/pbori/intersect.py @@ -1,10 +1,10 @@ -# -# intersect.py -# PolyBoRi -# -# Created by Michael Brickenstein on 2008-09-24. -# Copyright 2008 The PolyBoRi Team -# +r""" +intersect.py +PolyBoRi + +Created by Michael Brickenstein on 2008-09-24. +Copyright 2008 The PolyBoRi Team +""" from .gbcore import groebner_basis from .statistics import used_vars_set @@ -12,16 +12,19 @@ def intersect(i, j, **gb_opts): - """ + r""" This functions intersects two ideals. The first ring variable is used as helper variable for this intersection. It is assumed, that it doesn't occur in the ideals, and that we have an elimination ordering for this variables. Both assumptions are checked. - sage: from sage.rings.polynomial.pbori.brial.frontend import declare_ring - sage: from sage.rings.polynomial.pbori.brial import Block - sage: r=declare_ring(Block("x", 1000), globals()) - sage: x = r.variable - sage: intersect([x(1),x(2)+1],[x(1),x(2)]) - [x(1)] + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.frontend import declare_ring + sage: from sage.rings.polynomial.pbori.brial import Block + sage: r=declare_ring(Block("x", 1000), globals()) + sage: x = r.variable + sage: intersect([x(1),x(2)+1],[x(1),x(2)]) + [x(1)] """ if not i or not j: return [] diff --git a/src/sage/rings/polynomial/pbori/intpolys.py b/src/sage/rings/polynomial/pbori/intpolys.py index 7304deca98e..46fee86ddf3 100644 --- a/src/sage/rings/polynomial/pbori/intpolys.py +++ b/src/sage/rings/polynomial/pbori/intpolys.py @@ -32,19 +32,22 @@ def __coerce__(self, other): return (self, IntegerPolynomial(dict([(0, other)]))) def __add__(self, other): - """ - sage: from sage.rings.polynomial.pbori.brial import * - sage: r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS - sage: p=IntegerPolynomial(x(1)) - sage: p - {0: x(1)} - sage: p=p+p;p - {1: x(1)} - sage: p=p+x(2); p - {0: x(2), 1: x(1)} - sage: p+p - {1: x(2), 2: x(1)} - """ + r""" + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS + sage: p=IntegerPolynomial(x(1)) + sage: p + {0: x(1)} + sage: p=p+p;p + {1: x(1)} + sage: p=p+x(2); p + {0: x(2), 1: x(1)} + sage: p+p + {1: x(2), 2: x(1)} + """ if not isinstance(other, IntegerPolynomial): (self, other) = coerce(self, other) return self + other diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index b3a3f5a3205..ca6b5138a1f 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -79,9 +79,9 @@ def ll_encode(polys, reduce=False, prot=False, reduce_by_linear=True): def eliminate(polys, on_the_fly=False, prot=False, reduction_function=None, optimized=True): - """There exists an optimized variant, which reorders the variable in a different - ring. - """ + r""" + There exists an optimized variant, which reorders the variable in a different ring. + """ polys = [Polynomial(p) for p in polys] rest = [] linear_leads = [] @@ -207,37 +207,43 @@ def llnf(p): class RingMap(object): - """Define a mapping between two rings by common variable names. - - sage: from sage.rings.polynomial.pbori.brial.frontend import * - sage: to_ring = declare_ring([Block("x", 10)], globals()) - sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - sage: mapping = RingMap(to_ring, from_ring) - sage: (x(1)+1).navigation().value() - 6 - sage: mapping(x(1)+1) - x(1) + 1 - sage: mapping(x(1)+1).navigation().value() - 1 - sage: mapping.invert(mapping(x(1)+1)) - x(1) + 1 - sage: mapping.invert(mapping(x(1)+1)).navigation().value() - 6 - sage: mapping(y(1)+1) - Traceback (most recent call last): - ... - RuntimeError: Operands come from different manager. - """ - - def __init__(self, to_ring, from_ring): - """Initialize map by two given rings. + r""" + Define a mapping between two rings by common variable names. + TESTS:: + sage: from sage.rings.polynomial.pbori.brial.frontend import * sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) sage: mapping = RingMap(to_ring, from_ring) + sage: (x(1)+1).navigation().value() + 6 sage: mapping(x(1)+1) x(1) + 1 + sage: mapping(x(1)+1).navigation().value() + 1 + sage: mapping.invert(mapping(x(1)+1)) + x(1) + 1 + sage: mapping.invert(mapping(x(1)+1)).navigation().value() + 6 + sage: mapping(y(1)+1) + Traceback (most recent call last): + ... + RuntimeError: Operands come from different manager. + """ + + def __init__(self, to_ring, from_ring): + r""" + Initialize map by two given rings. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: mapping(x(1)+1) + x(1) + 1 """ def vars(ring): @@ -266,25 +272,29 @@ def indices(vars): self.from_map = BoolePolynomialVector(from_map) def __call__(self, poly): - """Execute the map to change rings. - - sage: from sage.rings.polynomial.pbori.brial.frontend import * - sage: to_ring = declare_ring([Block("x", 10)], globals()) - sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - sage: mapping = RingMap(to_ring, from_ring) - sage: mapping(x(1)+1) - x(1) + 1 + r""" + Execute the map to change rings. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: mapping(x(1)+1) + x(1) + 1 """ return substitute_variables(self.to_ring, self.to_map, poly) def invert(self, poly): - """Inverted map to initial ring. - - sage: from sage.rings.polynomial.pbori.brial.frontend import * - sage: to_ring = declare_ring([Block("x", 10)], globals()) - sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) - sage: mapping = RingMap(to_ring, from_ring) - sage: mapping.invert(mapping(x(1)+1)) - x(1) + 1 + r""" + Inverted map to initial ring. + + sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: to_ring = declare_ring([Block("x", 10)], globals()) + sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: mapping = RingMap(to_ring, from_ring) + sage: mapping.invert(mapping(x(1)+1)) + x(1) + 1 """ return substitute_variables(self.from_ring, self.from_map, poly) diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index f270061a1ba..dd537264c11 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -12,7 +12,9 @@ class GeneratorLimitExceeded(Exception): - """docstring for GeneratorLimitExceeded""" + r""" + Docstring for GeneratorLimitExceeded + """ def __init__(self, strat): #super(GeneratorLimitExceeded, self).__init__() @@ -26,8 +28,10 @@ def pkey(p): def build_and_print_matrices(v, strat): - """old solution using PIL, the currently used implementation is done in C++ - and plots the same matrices, as being calculated""" + r""" + Old solution using PIL, the currently used implementation is done in C++ + and plots the same matrices, as being calculated + """ treated = BooleSet() v = list(v) rows = 0 @@ -83,11 +87,14 @@ def build_and_print_matrices(v, strat): def multiply_polynomials(l, ring): - """ - sage: r=Ring(1000) - sage: x=r.variable - sage: multiply_polynomials([x(3), x(2)+x(5)*x(6), x(0), x(0)+1], r) - 0 + r""" + + TESTS:: + + sage: r=Ring(1000) + sage: x=r.variable + sage: multiply_polynomials([x(3), x(2)+x(5)*x(6), x(0), x(0)+1], r) + 0 """ l = [Polynomial(p) for p in l] @@ -101,7 +108,9 @@ def sort_key(p): def build_and_print_matrices_deg_colored(v, strat): - """old PIL solution using a different color for each degree""" + r""" + old PIL solution using a different color for each degree + """ if len(v) == 0: return @@ -642,13 +651,17 @@ def symmGB_F2_C(G, opt_exchange=True, def normal_form(poly, ideal, reduced=True): - """ Simple normal form computation of a polynomial against an ideal. - sage: from sage.rings.polynomial.pbori.brial import declare_ring, normal_form - sage: r=declare_ring(['x','y'], globals()) - sage: normal_form(x+y, [y],reduced=True) - x - sage: normal_form(x+y,[x,y]) - 0 + r""" + Simple normal form computation of a polynomial against an ideal. + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import declare_ring, normal_form + sage: r=declare_ring(['x','y'], globals()) + sage: normal_form(x+y, [y],reduced=True) + x + sage: normal_form(x+y,[x,y]) + 0 """ ring = poly.ring() strat = ReductionStrategy(ring) diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 83b4c00fce9..5874fd57815 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -1,12 +1,11 @@ -# -*- python -*- # coding=utf-8 +r""" +parallel.py +PolyBoRi -# parallel.py -# PolyBoRi -# -# Created by Michael Brickenstein on 2008-10-31. -# Copyright 2008 The PolyBoRi Team -# +Created by Michael Brickenstein on 2008-10-31. +Copyright 2008 The PolyBoRi Team +""" from .PyPolyBoRi import if_then_else, CCuddNavigator, BooleSet from .PyPolyBoRi import (Polynomial, Ring, WeakRingRef, Monomial, @@ -20,26 +19,32 @@ def to_fast_pickable(l): - """ - to_fast_pickable(l) converts a list of polynomials into a builtin Python value, which is fast pickable and compact. + r""" + Converts a list of polynomials into a builtin Python value, which is fast pickable and compact. + INPUT: - - a list of Boolean polynomials + + - a list of Boolean polynomials + OUTPUT: - It is converted to a tuple consisting of - - codes referring to the polynomials - - list of conversions of nodes. - The nodes are sorted, that - n occurs before n.else_branch(), n.then_branch() - Nodes are only listed, if they are not constant. - - A node is converted in this way: - 0 -> 0 - 1 -> 1 - if_then_else(v,t,e) -> (v, index of then branch +2, index of else branch +2) - The shift of +2 is for the constant values implicitly contained in the list. - Each code c refers to the c-2-th position in the conversion list, if c >=2, else to - the corresponding Boolean constant if c in {0, 1} - EXAMPLES: + + It is converted to a tuple consisting of + - codes referring to the polynomials + - list of conversions of nodes. + The nodes are sorted, that + n occurs before n.else_branch(), n.then_branch() + Nodes are only listed, if they are not constant. + + A node is converted in this way: + 0 -> 0 + 1 -> 1 + if_then_else(v,t,e) -> (v, index of then branch +2, index of else branch +2) + The shift of +2 is for the constant values implicitly contained in the list. + Each code c refers to the c-2-th position in the conversion list, if c >=2, else to + the corresponding Boolean constant if c in {0, 1} + + EXAMPLES:: + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x=r.variable @@ -101,14 +106,21 @@ def find_navs(nav): def from_fast_pickable(l, r): - """from_fast_pickable(l, ring) undoes the operation to_fast_pickable. The first argument is an object created by to_fast_pickable. + r""" + Undoes the operation to_fast_pickable. The first argument is an object created by to_fast_pickable. For the specified format, see the documentation of to_fast_pickable. The second argument is ring, in which this polynomial should be created. + INPUT: - see OUTPUT of to_fast_pickable + + See OUTPUT of to_fast_pickable + OUTPUT: - a list of Boolean polynomials - EXAMPLES: + + a list of Boolean polynomials + + EXAMPLES:: + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x = r.variable @@ -249,14 +261,20 @@ def pickle_ring(self): def groebner_basis_first_finished(I, *l): - """ + r""" + INPUT: - - I ideal - - l: keyword dictionaries, which will be keyword arguments to groebner_basis. + + - ``I`` -- ideal + - ``l`` -- keyword dictionaries, which will be keyword arguments to groebner_basis. + OUTPUT: - - tries to compute groebner_basis(I, **kwd) for kwd in l - - returns the result of the first terminated computation - EXAMPLES: + + - tries to compute groebner_basis(I, **kwd) for kwd in l + - returns the result of the first terminated computation + + EXAMPLES:: + sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring sage: r=Ring(1000) sage: ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] diff --git a/src/sage/rings/polynomial/pbori/parsegat.py b/src/sage/rings/polynomial/pbori/parsegat.py index 436978a0882..3c92d67c5d3 100644 --- a/src/sage/rings/polynomial/pbori/parsegat.py +++ b/src/sage/rings/polynomial/pbori/parsegat.py @@ -8,10 +8,14 @@ def _exists(): - """PolyBoRi convention: checking optional components for prerequisites here - - sage: _exists() - True + r""" + PolyBoRi convention: checking optional components for prerequisites here + + TESTS:: + + sage: from sage.rings.polynomial.pbori.parsegat import * + sage: _exists() + True """ try: import pyparsing @@ -86,7 +90,9 @@ def add_negated(str, log, tokens): class FamiliarityException(Exception): - """docstring for FamiliarityException""" + r""" + Docstring for FamiliarityException + """ def __init__(self): super(FamiliarityException, self).__init__() @@ -98,7 +104,9 @@ def fix_symbol_name(str, log, tokens): class DeterminingEquation(object): - """docstring for DeterminingEquation""" + r""" + Docstring for DeterminingEquation + """ def __init__(self, variable, mapped_to): super(DeterminingEquation, self).__init__() @@ -113,7 +121,9 @@ def _get_equation(self): # be careful: for next state/output we directly generate mapped_to + value # instead of introducing a variable and mapping it later class VariableManager(object): - """docstring for VariableManager""" + r""" + Docstring for VariableManager + """ def __init__(self, ring, prefix="", initialize="noinit", **kwd): super(VariableManager, self).__init__() diff --git a/src/sage/rings/polynomial/pbori/partial.py b/src/sage/rings/polynomial/pbori/partial.py index ffb75e4870d..4bdda1fb0b7 100644 --- a/src/sage/rings/polynomial/pbori/partial.py +++ b/src/sage/rings/polynomial/pbori/partial.py @@ -2,7 +2,9 @@ class PartialFunction(object): - """docstring for PartialFunction""" + r""" + Docstring for PartialFunction + """ def __init__(self, zeros, ones): super(PartialFunction, self).__init__() diff --git a/src/sage/rings/polynomial/pbori/plot.py b/src/sage/rings/polynomial/pbori/plot.py index dfdca8cb8d4..9387486b9c1 100644 --- a/src/sage/rings/polynomial/pbori/plot.py +++ b/src/sage/rings/polynomial/pbori/plot.py @@ -1,20 +1,22 @@ -# -*- python -*- # encoding: utf-8 -""" +r""" plot.py Created by Michael Brickenstein on 2008-10-17. Copyright (c) 2008 The PolyBoRi Team. - """ def _exists(): - """PolyBoRi convention: checking optional components for prerequisites here - - sage: _exists() - True + r""" + PolyBoRi convention: checking optional components for prerequisites here + + TESTS:: + + sage: from sage.rings.polynomial.pbori.plot import * + sage: _exists() + True """ try: import jinja2 @@ -129,14 +131,15 @@ def plot(p, filename, colored=True, format="png", highlight_monomial=None, fontsize=14, template_engine='jinja', landscape=False ): - """plots ZDD structure to in format + r""" + plots ZDD structure to in format - EXAMPLES: + EXAMPLES:: - sage: r=Ring(1000) - sage: x = r.variable - sage: plot(x(1)+x(0),"/dev/null", colored=True) - sage: plot(x(1)+x(0),"/dev/null", colored=False) + sage: r=Ring(1000) + sage: x = r.variable + sage: plot(x(1)+x(0),"/dev/null", colored=True) + sage: plot(x(1)+x(0),"/dev/null", colored=False) """ THICK_PEN = 5 highlight_path = dict() diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index 8eecf6cf693..27d20cde542 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -31,17 +31,22 @@ def helper(samples): def sparse_random_system(ring, number_of_polynomials, variables_per_polynomial, degree, random_seed=None): - """ - generates a system, which is sparse in the sense, that each polynomial - contains only a small subset of variables. In each variable that occurrs in a polynomial it is dense in the terms up to the given degree (every term occurs with probability 1/2). + r""" + Generates a system, which is sparse in the sense, that each polynomial + contains only a small subset of variables. In each variable that occurrs + in a polynomial it is dense in the terms up to the given degree + (every term occurs with probability 1/2). The system will be satisfiable by at least one solution. - sage: from sage.rings.polynomial.pbori.brial import * - sage: r=Ring(10) - sage: s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) - sage: [p.deg() for p in s] - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - sage: sorted(groebner_basis(s), reverse=True) - [x(0), x(1), x(2), x(3), x(4) + 1, x(5), x(6) + 1, x(7), x(8) + 1, x(9)] + + TESTS:: + + sage: from sage.rings.polynomial.pbori.brial import * + sage: r=Ring(10) + sage: s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) + sage: [p.deg() for p in s] + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + sage: sorted(groebner_basis(s), reverse=True) + [x(0), x(1), x(2), x(3), x(4) + 1, x(5), x(6) + 1, x(7), x(8) + 1, x(9)] """ if random_seed is not None: set_random_seed(random_seed) @@ -71,8 +76,11 @@ def sparse_random_system(ring, number_of_polynomials, def sparse_random_system_data_file_content( number_of_variables, **kwds): r""" - sage: sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) # doctest: +ELLIPSIS - "declare_ring(['x'+str(i) for in xrange(10)])\nideal=\\\n[...]\n\n" + + TESTS:: + + sage: sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) + declare_ring(['x'+str(i) for in xrange(10)])\nideal=\\\n[...]\n\n """ dummy_dict = dict() r = declare_ring(['x' + str(i) for i in range(number_of_variables)], diff --git a/src/sage/rings/polynomial/pbori/simplebb.py b/src/sage/rings/polynomial/pbori/simplebb.py index e9faf187a94..a80cf185444 100644 --- a/src/sage/rings/polynomial/pbori/simplebb.py +++ b/src/sage/rings/polynomial/pbori/simplebb.py @@ -3,7 +3,9 @@ def buchberger(l): - "calculates a (non minimal) Groebner basis" + r""" + Calculates a (non minimal) Groebner basis + """ l = interred(l) #for making sure, that every polynomial has a different leading term #needed for add_generator @@ -50,8 +52,14 @@ def less_than_n_solutions(ideal, n): def gauss(matrix): - """Toy Gaussian elimination. - Example: gauss([[0,1],[1,1]]) """ + r""" + Toy Gaussian elimination. + + EXAMPLES:: + + sage: from sage.rings.polynomial.pbori.simplebb import * + sage: gauss([[0,1],[1,1]]) + """ from .gbcore import groebner_basis def get_num(idx, vars): From 4e3ed8850f451ee6f65526434397cc880cd38f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 19 Aug 2020 14:03:57 +1200 Subject: [PATCH 237/379] one too many pbori. --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index 40420c5620a..49a53ed0398 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -65,7 +65,7 @@ """ from sage import all -from sage.rings.polynomial.pbori.pbori.pbori import * +from sage.rings.polynomial.pbori.pbori import * import weakref From 532aa78f084481a52e3d1127e569b356c64e66f1 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 12 Sep 2019 15:27:47 +0200 Subject: [PATCH 238/379] =?UTF-8?q?slightly=20faster=20eval=20of=20pol=20?= =?UTF-8?q?=E2=88=88=20=E2=84=9A[x]=20at=20Python=20int?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: sage: P. = QQ[] sage: p = (x+3)^10 sage: %timeit p(5r) The slowest run took 36.47 times longer than the fastest. This could mean that an intermediate result is being cached. 1000000 loops, best of 3: 464 ns per loop After: sage: %timeit p(5r) The slowest run took 43.97 times longer than the fastest. This could mean that an intermediate result is being cached. 1000000 loops, best of 3: 428 ns per loop --- .../rings/polynomial/polynomial_rational_flint.pyx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 67297132e61..0ccb2ac6ae6 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -481,6 +481,8 @@ cdef class Polynomial_rational_flint(Polynomial): cdef Polynomial_rational_flint f cdef Rational r cdef mpz_t tmpz + cdef fmpz_t tmpfz + cdef fmpq_t tmpfq if len(x) == 1: a = x[0] @@ -506,10 +508,13 @@ cdef class Polynomial_rational_flint(Polynomial): elif isinstance(a, int): r = Rational.__new__(Rational) sig_str("FLINT exception") - mpz_init(tmpz) - mpz_set_si(tmpz, PyInt_AS_LONG(a)) - fmpq_poly_evaluate_mpz(r.value, self.__poly, tmpz) - mpz_clear(tmpz) + fmpz_init(tmpfz) + fmpq_init(tmpfq) + fmpz_set_si(tmpfz, PyInt_AS_LONG(a)) + fmpq_poly_evaluate_fmpz(tmpfq, self.__poly, tmpfz) + fmpq_get_mpq(r.value, tmpfq) + fmpq_clear(tmpfq) + fmpz_clear(tmpfz) sig_off() return r From 171dc18a3d5a344411ed05ef7acc16b134ce356d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Wed, 19 Aug 2020 15:17:19 +0200 Subject: [PATCH 239/379] #30390: Update PyPI url and a few more --- src/doc/en/faq/faq-contribute.rst | 50 +++++++++---------- src/doc/en/thematic_tutorials/profiling.rst | 13 +++-- src/doc/it/faq/faq-contribute.rst | 50 +++++++++---------- src/mac-app/tools/createDSStore/README.txt | 4 +- .../geometry/polyhedron/backend_normaliz.py | 4 +- src/sage/geometry/polyhedron/base_QQ.py | 4 +- src/sage/geometry/polyhedron/base_ZZ.py | 4 +- src/sage/misc/package.py | 2 +- 8 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index bc70ab6c145..212855faed9 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -30,7 +30,7 @@ mailing list or hang around on the `freenode `_. While you are getting to know the community, grab a copy of the Sage source and familiarize yourself with the -`git `_ version control system. +`git `_ version control system. The best way to become familiar with the Sage development process is to choose a ticket from the @@ -61,14 +61,14 @@ evil." If you do not know Python, you should start learning that language. A good place to start is the -`Python Official Tutorial `_ +`Python Official Tutorial `_ and other documents in the `Python standard documentation `_. Another good place to take a look at is -`Dive Into Python `_ +`Dive Into Python `_ by Mark Pilgrim, which may be pretty helpful on some specific topics such as test-driven development. The book -`Building Skills in Python `_ +`Building Skills in Python `_ by Steven F. Lott is suitable for anyone who is already comfortable with programming. @@ -95,7 +95,7 @@ useful in fields other than technical writing itself. A main point about technical writing is that you communicate a technical subject to beginners, so keep technical jargon to a minimum. Darrell Anderson has written some -`tips on technical writing `_, +`tips on technical writing `_, which we highly recommend. For the graphic designers or the artistically creative, you can @@ -109,9 +109,9 @@ Italian, or help out with translating the official Sage tutorial to Italian. The above is a very short list. There are many, many more ways in -which you can help out. Feel free to send an email to the `sage-devel -`_ mailing list to ask -about possible ways in which you could help out, or to suggest a +which you can help out. Feel free to send an email to the +`sage-devel `_ mailing list +to ask about possible ways in which you could help out, or to suggest a project idea. @@ -123,17 +123,17 @@ resources can be found by a web search. **General resources** -* `Cython `_ -* `pep8 `_ -* `py2depgraph `_ -* `pycallgraph `_ +* `Cython `_ +* `pep8 `_ +* `pydeps `_ +* `pycallgraph `_ * `PyChecker `_ -* `PyFlakes `_ -* `Pylint `_ +* `PyFlakes `_ +* `Pylint `_ * `Python `_ home page and the `Python standard documentation `_ * `Snakefood `_ -* `Sphinx `_ +* `Sphinx `_ * `XDot `_ **Tutorials and books** @@ -143,18 +143,18 @@ resources can be found by a web search. * `Dive Into Python 3 `_ by Mark Pilgrim * `Fast Numerical Computations with Cython `_ by Dag Sverre Seljebotn -* `Official Python Tutorial `_ +* `Official Python Tutorial `_ **Articles and HOWTOs** -* `decorator `_ -* `Functional Programming HOWTO `_ +* `decorator `_ +* `Functional Programming HOWTO `_ by A. M. Kuchling -* `Python Functional Programming for Mathematicians `_ +* `Python Functional Programming for Mathematicians `_ by Minh Van Nguyen -* `Regular Expression HOWTO `_ +* `Regular Expression HOWTO `_ by A. M. Kuchling -* `reStructuredText `_ +* `reStructuredText `_ Are there any coding conventions I need to follow? """""""""""""""""""""""""""""""""""""""""""""""""" @@ -162,7 +162,7 @@ Are there any coding conventions I need to follow? You should follow the standard Python conventions as documented at :pep:`8` and :pep:`257`. Also consult the Sage Developer's Guide, especially the chapter -`Conventions for Coding in Sage `_. +`Conventions for Coding in Sage `_. I submitted a bug fix to the trac server several weeks ago. Why are you ignoring my patch? @@ -190,7 +190,7 @@ tips on making your patch easy to review: * If there are more than one patch, have you clearly stated the order in which those patches are to be applied? * Does your patch - `follow relevant conventions `_ + `follow relevant conventions `_ as documented in the Developer's Guide? If your patch stands no chance of being merged in the Sage source @@ -265,8 +265,8 @@ necessity to import what you need. Type: function [...] - File: /home/florent/src/Sage/sage/local/lib/python2.6/site-packages/sage/rings/polynomial/polynomial_ring_constructor.py + File: /path_to_sage_root/sage/local/lib/python3.7/site-packages/sage/rings/polynomial/polynomial_ring_constructor.py [...] -.. _afterword: http://doc.sagemath.org/html/en/tutorial/afterword.html +.. _afterword: https://doc.sagemath.org/html/en/tutorial/afterword.html diff --git a/src/doc/en/thematic_tutorials/profiling.rst b/src/doc/en/thematic_tutorials/profiling.rst index 39ebeed032b..4f58a499776 100644 --- a/src/doc/en/thematic_tutorials/profiling.rst +++ b/src/doc/en/thematic_tutorials/profiling.rst @@ -57,7 +57,7 @@ computation, as well as the time spent on each of them:: The most time-consuming functions should appear on the top. A description of the different columns is `available here -`_. +`_. .. NOTE:: @@ -73,7 +73,7 @@ further inspection:: 2547 For more information see ``%prun?`` or `this page -`__. +`__. **Visualize the statistics:** you can obtain a more graphical output with `RunSnake `_ and Sage's @@ -84,7 +84,7 @@ function :func:`runsnake`:: Python-level line-by-line profiling: %lprun ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -With `line_profiler `_ and its +With `line_profiler `_ and its ``%lprun`` magic, you can find out which lines of one (or many) functions are the most time-consuming. The syntax is the following:: @@ -115,7 +115,7 @@ C-level function calls: %crun With ``%crun``, you can obtain the list of all C functions involved in a computation, as well as the time spent on each of them. You will need to have -`the Google performance analysis tools `_ +`the Google performance analysis tools `_ installed on your system:: sage: %crun p=random_prime(2**500) @@ -135,9 +135,8 @@ C-level line-by-line profiling: perf (Linux only) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If your code is written in C or in Cython, you can find out line-by-line which -are the most costly using `perf -`_ (included in the Ubuntu -package ``linux-tools``). +are the most costly using `perf `_ +(included in the Ubuntu package ``linux-tools``). The easiest way to use it is to run some (very long) computation in Sage, and to type in a console diff --git a/src/doc/it/faq/faq-contribute.rst b/src/doc/it/faq/faq-contribute.rst index b0a0e1a6657..b953ef80b40 100644 --- a/src/doc/it/faq/faq-contribute.rst +++ b/src/doc/it/faq/faq-contribute.rst @@ -27,7 +27,7 @@ Puoi entrare nella lista email `sage-devel `_ o nel canale IRC ``#sage-devel`` su `freenode `_. Mentre inizi a conoscere la comunità prendi una copia del codice sorgente di Sage -e familiarizza con `git `_, un software per il controllo +e familiarizza con `git `_, un software per il controllo versione. Il migiol mode per diventare familiare con il processo di sviluppo di Sage @@ -59,15 +59,15 @@ radice di ogni male". Se non conosci Python dovresti iniziare ad imparare il linguaggio. Un buon posto dove iniziare è il -`Tutorial Ufficiale per Python `_ +`Tutorial Ufficiale per Python `_ e altra documentazione si trova in `Documentazione standard di Python `_. Un altro posto da guardare è al link -`Dive Into Python `_ di Marc Pilgrim, +`Dive Into Python `_ di Marc Pilgrim, che può essere veramente d'aiuto su temi specifici come lo sviluppo guidato dai test. Il libro -`Building Skills in Python `_ -di Stephen F.Lott è adatto a chiunque sia già a suo agio nel programmare. +`Building Skills in Python `_ +di Steven F. Lott è adatto a chiunque sia già a suo agio nel programmare. Se desideri, puoi iniziare a imparare Python usando Sage. Tuttavia, è utile sapere cosa è semplice Python e quando Sage sta usando la @@ -89,7 +89,8 @@ ai principianti, un'abilità che è utile anche in campi estranei alla stesura di documentazione tecnica. Un aspetto fondamentale della documentazione tecnica è che espone un dato tecnico a dei principianti, pertanto il gergo tecnico dev'essere ridotto al minimo. Darrell Anderson ha scritto -`alcuni suggerimenti sulla scrittura di documentazione tecnica `_, +`alcuni suggerimenti sulla scrittura di documentazione tecnica +`_, il quale consigliamo vivamente. Per i designer grafici o gli artisti c'è la possibilità di aiutare migliorando @@ -115,18 +116,18 @@ Ulteriori risorse possono essere trovate cercando sul web. **Risorse generali** -* `Cython `_ -* `pep8 `_ -* `py2depgraph `_ -* `pycallgraph `_ +* `Cython `_ +* `pep8 `_ +* `pydeps `_ +* `pycallgraph `_ * `PyChecker `_ -* `PyFlakes `_ -* `Pylint `_ +* `PyFlakes `_ +* `Pylint `_ * `Python `_ home page e la `Documentazione standard su Python `_ * `Snakefood `_ -* `Sphinx `_ -* `XDot `_ +* `Sphinx `_ +* `XDot `_ **Tutorial e libri** @@ -135,18 +136,18 @@ Ulteriori risorse possono essere trovate cercando sul web. * `Dive Into Python 3 `_ di Mark Pilgrim * `Fast Numerical Computations with Cython `_ di Dag Sverre Seljebotn -* `Tutorial ufficiale di Python `_ +* `Tutorial ufficiale di Python `_ **Articoli e HOWTO** -* `decorator `_ -* `Functional Programming HOWTO `_ +* `decorator `_ +* `Functional Programming HOWTO `_ di A. M. Kuchling -* `Python Functional Programming for Mathematicians `_ +* `Python Functional Programming for Mathematicians `_ di Minh Van Nguyen -* `Regular Expression HOWTO `_ +* `Regular Expression HOWTO `_ di A. M. Kuchling -* `reStructuredText `_ +* `reStructuredText `_ Ci sono delle convenzioni di scrittura del codice sorgente che devo seguire? @@ -155,7 +156,7 @@ Ci sono delle convenzioni di scrittura del codice sorgente che devo seguire? Dovresti seguire le convenzioni standard di Python come documentato in :pep:`8` e :pep:`257`. Consulta anche la Guida dello Sviluppo Sage, specialmente il capitolo -`Convenzioni per scrivere codice sorgente in Sage `_. +`Convenzioni per scrivere codice sorgente in Sage `_. Ho inviato al server trac una correzione molte settimane fa. Perchè la state ignorando? @@ -183,7 +184,7 @@ Ecco alcuni suggerimenti su come rendere la tua correzione facile da esaminare * Se vi sono più correzioni, hai indicato chiaramente l'ordine in cui devono essere applicate ? * La tua correzione segue le - `convenzioni importanti `_ + `convenzioni importanti `_ indicate nella "Guida dello sviluppatore"? Se la tua correzione non ha la possibilità di essere aggiunta nell'albero dei @@ -252,9 +253,8 @@ preparser di Sage) e la necessità di importare quello che ti serve. Type: function [...] - File: /home/florent/src/Sage/sage/local/lib/python2.6/site-packages/sage/rings/ - polynomial/polynomial_ring_constructor.py + File: /path_to_sage_root/sage/local/lib/python3.7/site-packages/sage/rings/polynomial/polynomial_ring_constructor.py [...] -.. _afterword: http://www.sagemath.org/doc/tutorial/afterword.html +.. _afterword: https://doc.sagemath.org/html/en/tutorial/afterword.html diff --git a/src/mac-app/tools/createDSStore/README.txt b/src/mac-app/tools/createDSStore/README.txt index e4ae1d9cdc8..a2f3e35b1b4 100644 --- a/src/mac-app/tools/createDSStore/README.txt +++ b/src/mac-app/tools/createDSStore/README.txt @@ -1,4 +1,4 @@ -All dependencies of createDSStore are available on http://pypi.python.org/ and +All dependencies of createDSStore are available on https://pypi.org/ and are included here in the following versions and under the following licenses: six 1.11.0 MIT @@ -6,5 +6,5 @@ mac_alias 2.0.6 MIT biplist 1.0.3 BSD ds_store 1.1.2 MIT -There are no dependencies on Mac OS X specific libraries so the code can +There are no dependencies on macOS specific libraries, so the code can be run from Linux as well. diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 95928812574..acc49a737ee 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -4,7 +4,7 @@ .. NOTE:: - This backend requires `PyNormaliz `_. + This backend requires `PyNormaliz `_. To install PyNormaliz, type :code:`sage -i pynormaliz` in the terminal. AUTHORS: @@ -20,7 +20,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import, print_function diff --git a/src/sage/geometry/polyhedron/base_QQ.py b/src/sage/geometry/polyhedron/base_QQ.py index 917c617ac76..979c84550a7 100644 --- a/src/sage/geometry/polyhedron/base_QQ.py +++ b/src/sage/geometry/polyhedron/base_QQ.py @@ -281,7 +281,7 @@ def ehrhart_polynomial(self, engine=None, variable='t', verbose=False, .. SEEALSO:: :mod:`~sage.interfaces.latte` the interface to LattE Integrale - `PyNormaliz `_ + `PyNormaliz `_ EXAMPLES: @@ -450,7 +450,7 @@ def ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False, .. SEEALSO:: :mod:`~sage.interfaces.latte` the interface to LattE Integrale - `PyNormaliz `_ + `PyNormaliz `_ .. WARNING:: diff --git a/src/sage/geometry/polyhedron/base_ZZ.py b/src/sage/geometry/polyhedron/base_ZZ.py index 5f346963bbe..f4c41edab93 100644 --- a/src/sage/geometry/polyhedron/base_ZZ.py +++ b/src/sage/geometry/polyhedron/base_ZZ.py @@ -9,7 +9,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import @@ -354,7 +354,7 @@ def ehrhart_polynomial(self, engine=None, variable='t', verbose=False, dual=None .. SEEALSO:: :mod:`~sage.interfaces.latte` the interface to LattE Integrale - `PyNormaliz `_ + `PyNormaliz `_ EXAMPLES: diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index c27b1e6e455..2b065b88bdd 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -57,7 +57,7 @@ # Python 2.7 from urllib2 import urlopen, URLError -DEFAULT_PYPI = 'https://pypi.python.org/pypi' +DEFAULT_PYPI = 'https://pypi.org/project' def pkgname_split(name): r""" From b94eb15896ac4a44a5d6939637bf1f7bf27c7932 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 19 Aug 2020 17:20:15 +0200 Subject: [PATCH 240/379] added antipodal and folded graph methods; most docstrings done --- src/doc/en/reference/references/index.rst | 3 + src/sage/graphs/graph.py | 118 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 6b5e66fafb6..85ef2f56808 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4787,6 +4787,9 @@ REFERENCES: The flush statistic on semistandard Young tableaux. :arXiv:`1401.1185` +.. [Sam2012] \P. Samanta: *Antipodal Graphs* + :doi:`10.13140/RG.2.2.28238.46409` + .. [Sch1990] Schnyder, Walter. *Embedding Planar Graphs on the Grid*. Proc. 1st Annual ACM-SIAM Symposium on Discrete Algorithms, San Francisco (1994), pp. 138-147. diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 9de2c628e93..9e96454b397 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -9361,6 +9361,124 @@ def arboricity(self, certificate=False): else: return len(P) + def is_antipodal(self): + r""" + Return whether this graph is antipodal. + + A graph `G` of diameter `d` is said to be antipodal if its distance-`d` + graph is a disjoint union of cliques. + + EXAMPLES:: + + sage: G = graphs.JohnsonGraph(10, 5) + sage: G.is_antipodal() + True + sage: H = G.folded_graph() + sage: H.is_antipodal() + False + + REFERENCES: + + See [BCN1989]_ p. 438 or [Sam2012]_ for this definition of antipodal + graphs. + + TESTS:: + + sage: G = graphs.PetersenGraph() + sage: G.is_antipodal() + False + sage: G = graphs.HammingGraph(7, 2) + sage: G.is_antipodal() + True + sage: G = Graph([(0,1), (2, 3)]) + sage: G.is_antipodal() + False + sage: G = Graph(4) + sage: G.is_antipodal() + True + sage: graphs.CompleteGraph(5).is_antipodal() + True + """ + G = self.distance_graph(self.diameter()) + + vertexSet = set(G) + while vertexSet: + v = vertexSet.pop() + + # all neighbours of v should be in the same clique as v + clique = set(G.neighbor_iterator(v, closed=True)) + for u in clique: + if set(G.neighbor_iterator(u, closed=True)) != clique: + return False + + vertexSet.difference_update(clique) + + return True + + def folded_graph(self): + r""" + Return the antipodal fold of this graph. + + Given an antipodal graph `G` let `G_d` be its distance-`d` graph. + Then the folded graph of `G` has a vertex for each maximal clique + of `G_d` and two cliques are adjacent if there is an edge in `G` + connecting the two. + + OUTPUT: + + This function returns a new graph and ``self`` is not touched. + + .. NOTE:: + + The input is expected to be an antipodal graph. + You can check that a graph is antipodal using + :meth:`sage.graphs.graph.is_antipodal`. + + EXAMPLES:: + + sage: G = graphs.JohnsonGraph(10, 5) + sage: H = G.folded_graph(); H + Folded Johnson graph with parameters 10,5: Graph on 126 vertices + sage: Gd = G.distance_graph(G.diameter()) + sage: all(i == 1 for i in Gd.degree()) + True + sage: H.is_distance_regular(True) + ([25, 16, None], [None, 1, 4]) + + REFERENCES: + + See [BCN1989]_ p. 438 or [Sam2012]_ for this definition of folded graph. + + TESTS:: + + sage: G = Graph(4) + sage: G.folded_graph() + """ + G = self.distance_graph(self.diameter()) + + vertices = set(G) + newVertices = {} + numCliques = 0 + while vertices: + v = vertices.pop() + clique = frozenset(G.neighbor_iterator(v, closed=True)) + newVertices[numCliques] = clique + numCliques += 1 + vertices.difference_update(clique) + + # now newVertices is a map {0, ..., numCliques-1} -> antipodal classes + edges = [] + for i, j in itertools.combinations(range(numCliques), 2): + if any(self.has_edge(u, v) for u, v in + itertools.product(newVertices[i], newVertices[j])): + edges.append((i, j)) + + H = Graph([range(numCliques), edges], format='vertices_and_edges') + H.name(f"Folded {self.name()}") + return H + + + # Aliases to functions defined in other modules from sage.graphs.weakly_chordal import is_long_hole_free, is_long_antihole_free, is_weakly_chordal from sage.graphs.asteroidal_triples import is_asteroidal_triple_free From 9e206a36b171ca515ef6e298f3acb15ee3f13f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Wed, 19 Aug 2020 17:23:38 +0200 Subject: [PATCH 241/379] 30390-fix-pypi-api-url --- src/sage/misc/package.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 2b065b88bdd..63f51d83630 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -57,7 +57,7 @@ # Python 2.7 from urllib2 import urlopen, URLError -DEFAULT_PYPI = 'https://pypi.org/project' +DEFAULT_PYPI = 'https://pypi.org/pypi' def pkgname_split(name): r""" @@ -117,7 +117,7 @@ def pip_remote_version(pkg, pypi_url=DEFAULT_PYPI, ignore_URLError=False): except URLError: if ignore_URLError: import warnings - warnings.warn("failed to fetch the version of pkg={!r} at {}".format(pkg, pypi_url)) + warnings.warn("failed to fetch the version of pkg={!r} at {}".format(pkg, url)) return else: raise From 804c4b17d87e86efbb5cd05b6beb45f63d0b3047 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 19 Aug 2020 17:53:45 +0200 Subject: [PATCH 242/379] fixed bug; finished docstrings/doctests --- src/sage/graphs/graph.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 9e96454b397..54816b5c0fe 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -9307,6 +9307,7 @@ def most_common_neighbors(self, nonedgesonly=True): if M[v, w] == maximum: output.append((verts[v], verts[w])) return output + @doc_index("Leftovers") def arboricity(self, certificate=False): r""" @@ -9361,6 +9362,7 @@ def arboricity(self, certificate=False): else: return len(P) + @doc_index("Graph properties") def is_antipodal(self): r""" Return whether this graph is antipodal. @@ -9415,6 +9417,7 @@ def is_antipodal(self): return True + @doc_index("Leftovers") def folded_graph(self): r""" Return the antipodal fold of this graph. @@ -9424,6 +9427,10 @@ def folded_graph(self): of `G_d` and two cliques are adjacent if there is an edge in `G` connecting the two. + .. SEEALSO:: + + :meth:`sage.graphs.graph.is_antipodal` + OUTPUT: This function returns a new graph and ``self`` is not touched. @@ -9445,14 +9452,26 @@ def folded_graph(self): sage: H.is_distance_regular(True) ([25, 16, None], [None, 1, 4]) + This method doesn't check if the graph is antipodal:: + + sage: G = graphs.PetersenGraph() + sage: G.is_antipodal() + False + sage: G.folded_graph() # some garbage + Folded Petersen graph: Graph on 2 vertices + REFERENCES: See [BCN1989]_ p. 438 or [Sam2012]_ for this definition of folded graph. TESTS:: - sage: G = Graph(4) + sage: G = Graph(5) + sage: G.folded_graph() + Folded Graph: Graph on 1 vertex + sage: G = graphs.CompleteGraph(5) sage: G.folded_graph() + Folded Complete graph: Graph on 1 vertex """ G = self.distance_graph(self.diameter()) @@ -9474,7 +9493,8 @@ def folded_graph(self): edges.append((i, j)) H = Graph([range(numCliques), edges], format='vertices_and_edges') - H.name(f"Folded {self.name()}") + name = self.name() if self.name() != "" else "Graph" + H.name(f"Folded {name}") return H From abe331ef802f65a1b6bd8d3c8d6728bc6b79a9f1 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 19 Aug 2020 19:10:35 +0100 Subject: [PATCH 243/379] some modernisation (patch -> branch), etc --- src/doc/en/faq/faq-contribute.rst | 35 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index 212855faed9..e484443e4d9 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -165,50 +165,47 @@ Also consult the Sage Developer's Guide, especially the chapter `Conventions for Coding in Sage `_. -I submitted a bug fix to the trac server several weeks ago. Why are you ignoring my patch? +I submitted a bug fix to the trac server several weeks ago. Why are you ignoring my branch? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -We are not trying to ignore your patch. People who work on Sage do so +We are not trying to ignore your branch. Most people who work on Sage do so in their free time. With hundreds of open tickets of varying degrees of impacts on the whole Sage community, people who work on tickets need to prioritize their time and work on those tickets that interest them. Sometimes you may be the only person who understands your -patch. In that case, you are encouraged to take extra care to make it -as easy as possible for anyone to review your patch. Here are some -tips on making your patch easy to review: +branch. In that case, you are encouraged to take extra care to make it +as easy as possible for anyone to review. Here are some +tips on making your branch easy to review: -* Have you clearly described the problem your patch is trying to +* Have you clearly described the problem your branch is trying to solve? * Have you provided any background information relevant to the problem your patch is trying to solve? Such information include links to online resources and any relevant papers, books and reference materials. -* Have you clearly described how your patch solves the problem under +* Have you clearly described how your branch solves the problem under consideration? -* Have you clearly described how to test the changes in your patch? -* Have you listed any tickets that your patch depends on? -* If there are more than one patch, have you clearly stated the order - in which those patches are to be applied? -* Does your patch +* Have you clearly described how to test the changes in your branch? +* Have you listed any tickets that your branch depends on? +* Is your branch based on a recent (peferably, the lastest) Sage beta version? +* Does your branch `follow relevant conventions `_ as documented in the Developer's Guide? -If your patch stands no chance of being merged in the Sage source -tree, we will not ignore your patch but simply close the relevant +If your branch stands no chance of being merged in the Sage source +tree, we will not ignore your branch but simply close the relevant ticket with an explanation why we cannot include your changes. -When and how might I remind the Sage community of a patch I care about? +When and how might I remind the Sage community of a branch I care about? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" You are encouraged to take extra care in how you remind the Sage -community of a patch you want to get merged into the Sage source +community of a branch/patch you want to get merged into the Sage source tree. There might be an upcoming bug squash sprint or an upcoming Sage Days workshop that relates to your patch. Monitor the relevant Sage mailing lists and respond politely to any relevant email threads, with -clear explanation on why your patch is relevant. Monitor the -``#sage-devel`` IRC channel, taking care to strategically time your -reminders. +clear explanation on why your patch is relevant. I wrote some Sage code and I want it to be integrated into Sage. However, after renaming my file ``a.sage`` to ``a.py``, I got syntax errors. Do I have to rewrite all my code in Python instead of Sage? From 563e959d6dcf27f9352f5ae3590b2b35148fe2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 11:11:11 +1200 Subject: [PATCH 244/379] add miising import of lazy_import --- src/sage/rings/polynomial/pbori/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/polynomial/pbori/__init__.py b/src/sage/rings/polynomial/pbori/__init__.py index c50969fdf72..834c1c1b728 100644 --- a/src/sage/rings/polynomial/pbori/__init__.py +++ b/src/sage/rings/polynomial/pbori/__init__.py @@ -48,6 +48,7 @@ def plist(a, b): # ... any from below? ... # Deprecated reimports +from sage.misc.lazy_import import lazy_import lazy_import('sage.rings.polynomial.pbori.pbori', ['BooleConstant', 'BooleSet', From 7cb8ba18a4c983f4cf0214e4d309f6131fd91223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 12:23:56 +1200 Subject: [PATCH 245/379] remove excessive .brial from import. The initial replacement was wrong. --- .../polynomial/multi_polynomial_sequence.py | 6 +- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 2 +- src/sage/rings/polynomial/pbori/addition.py | 10 +- src/sage/rings/polynomial/pbori/cnf.py | 14 +- src/sage/rings/polynomial/pbori/context.py | 4 +- .../polynomial/pbori/easy_polynomials.py | 4 +- src/sage/rings/polynomial/pbori/fglm.py | 8 +- src/sage/rings/polynomial/pbori/frontend.py | 2 +- src/sage/rings/polynomial/pbori/gbcore.py | 2 +- src/sage/rings/polynomial/pbori/intersect.py | 4 +- src/sage/rings/polynomial/pbori/intpolys.py | 2 +- src/sage/rings/polynomial/pbori/ll.py | 8 +- src/sage/rings/polynomial/pbori/nf.py | 2 +- src/sage/rings/polynomial/pbori/parallel.py | 6 +- src/sage/rings/polynomial/pbori/pbori.pyx | 224 +++++++++--------- src/sage/rings/polynomial/pbori/randompoly.py | 2 +- src/sage/sat/converters/__init__.py | 2 +- 17 files changed, 151 insertions(+), 151 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 91d096dc9d9..af95524f3b7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1249,8 +1249,8 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc This is called "massaging" in [BCJ2007]_. """ from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing - from sage.rings.polynomial.pbori.brial import gauss_on_polys - from sage.rings.polynomial.pbori.brial.ll import eliminate,ll_encode,ll_red_nf_redsb + from sage.rings.polynomial.pbori import gauss_on_polys + from sage.rings.polynomial.pbori.ll import eliminate,ll_encode,ll_red_nf_redsb R = self.ring() @@ -1534,7 +1534,7 @@ def reduced(self): R = self.ring() if isinstance(R, BooleanPolynomialRing): - from sage.rings.polynomial.pbori.brial.interred import interred as inter_red + from sage.rings.polynomial.pbori.interred import interred as inter_red l = [p for p in self if not p==0] l = sorted(inter_red(l, completely=True), reverse=True) return PolynomialSequence(l, R, immutable=True) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index 49a53ed0398..cd472ccaf03 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -10,7 +10,7 @@ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) sage: x0>x1 True diff --git a/src/sage/rings/polynomial/pbori/addition.py b/src/sage/rings/polynomial/pbori/addition.py index ae7318c5262..51433521eaa 100644 --- a/src/sage/rings/polynomial/pbori/addition.py +++ b/src/sage/rings/polynomial/pbori/addition.py @@ -10,7 +10,7 @@ def add_bits_old(bits): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(10) sage: add_bits_old([r.variable(i) for i in xrange(3)]) [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] @@ -41,7 +41,7 @@ def add_bits(bits): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(10) sage: add_bits([r.variable(i) for i in xrange(3)]) [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] @@ -70,7 +70,7 @@ def add_bit_expressions(bit_expressions): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(20) sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] @@ -100,7 +100,7 @@ def add_words(words): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(1000) sage: add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] @@ -131,7 +131,7 @@ def multiply_by_addition(word_a, word_b): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import Ring + sage: from sage.rings.polynomial.pbori import Ring sage: r=Ring(1000) sage: x = r.variable sage: n=7 diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py index 8b5eb453e0e..74dc918eb2c 100644 --- a/src/sage/rings/polynomial/pbori/cnf.py +++ b/src/sage/rings/polynomial/pbori/cnf.py @@ -18,7 +18,7 @@ def zero_blocks(self, f): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) sage: e = CNFEncoder(r) sage: e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) @@ -85,7 +85,7 @@ def clauses(self, f): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) sage: e = CNFEncoder(r) sage: e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS @@ -112,7 +112,7 @@ def polynomial_clauses(self, f): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) sage: e = CNFEncoder(r) sage: e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) @@ -154,7 +154,7 @@ def dimacs_encode_polynomial(self, p): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: d=dict() sage: r = declare_ring(["x", "y", "z"], d) sage: e = CNFEncoder(r) @@ -172,7 +172,7 @@ def dimacs_cnf(self, polynomial_system): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) sage: e = CNFEncoder(r) sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) @@ -201,7 +201,7 @@ def dimacs_encode_polynomial(self, p): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: d=dict() sage: r = declare_ring(["x", "y", "z"], d) sage: e = CryptoMiniSatEncoder(r) @@ -238,7 +238,7 @@ def dimacs_cnf(self, polynomial_system): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) sage: e = CryptoMiniSatEncoder(r) sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) diff --git a/src/sage/rings/polynomial/pbori/context.py b/src/sage/rings/polynomial/pbori/context.py index 323d2475682..e61ed81a0ce 100644 --- a/src/sage/rings/polynomial/pbori/context.py +++ b/src/sage/rings/polynomial/pbori/context.py @@ -31,7 +31,7 @@ class FactoryContext(object): EXAMPLES:: sage: r = Ring(1000) - sage: from sage.rings.polynomial.pbori.brial import Variable + sage: from sage.rings.polynomial.pbori import Variable sage: def var(idx): return Variable(idx, r) sage: with FactoryContext(Variable, var): ... print Variable(17) @@ -73,7 +73,7 @@ class RingContext(object): EXAMPLES:: sage: r = Ring(1000) - sage: from sage.rings.polynomial.pbori.brial import Variable + sage: from sage.rings.polynomial.pbori import Variable sage: print Variable(17, r) x(17) sage: with RingContext(r): diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py index 64ca30ef7a2..96a16692e15 100644 --- a/src/sage/rings/polynomial/pbori/easy_polynomials.py +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -8,7 +8,7 @@ def easy_linear_polynomials(p): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: easy_linear_polynomials(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] sage: easy_linear_polynomials(x(1)*x(2) + 0) @@ -33,7 +33,7 @@ def easy_linear_polynomials_via_interpolation(p): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index f29bbc65d5c..ab0ef25102f 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -30,7 +30,7 @@ def fglm(I, from_ring, to_ring): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import OrderCode sage: dp_asc = OrderCode.dp_asc sage: r=declare_ring(['x','y','z'],dict()) sage: old_ring = r @@ -52,9 +52,9 @@ def vars_real_divisors(monomial, monomial_set): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import OrderCode sage: dp_asc = OrderCode.dp_asc - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x = r.variable sage: b=BooleSet([x(1)*x(2),x(2)]) @@ -72,7 +72,7 @@ def m_k_plus_one(completed_elements, variables): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import OrderCode + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import OrderCode sage: dp_asc = OrderCode.dp_asc sage: r=Ring(1000) sage: x = r.variable diff --git a/src/sage/rings/polynomial/pbori/frontend.py b/src/sage/rings/polynomial/pbori/frontend.py index ca41fc13c0c..8da780e3218 100644 --- a/src/sage/rings/polynomial/pbori/frontend.py +++ b/src/sage/rings/polynomial/pbori/frontend.py @@ -18,7 +18,7 @@ sage: x(9999) + x(9999) 0 - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: context = dict(globals()) sage: polybori_start(context) ipbori... diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index f187abcea87..3bb8ab0e60b 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -387,7 +387,7 @@ def variety_size_from_gb(I): def other_ordering_pre(I, option_set, kwds): """ - sage: from sage.rings.polynomial.pbori.brial.blocks import declare_ring + sage: from sage.rings.polynomial.pbori.blocks import declare_ring sage: r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) sage: id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] sage: groebner_basis(id) diff --git a/src/sage/rings/polynomial/pbori/intersect.py b/src/sage/rings/polynomial/pbori/intersect.py index c7b1405375f..774ca868d11 100644 --- a/src/sage/rings/polynomial/pbori/intersect.py +++ b/src/sage/rings/polynomial/pbori/intersect.py @@ -19,8 +19,8 @@ def intersect(i, j, **gb_opts): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.frontend import declare_ring - sage: from sage.rings.polynomial.pbori.brial import Block + sage: from sage.rings.polynomial.pbori.frontend import declare_ring + sage: from sage.rings.polynomial.pbori import Block sage: r=declare_ring(Block("x", 1000), globals()) sage: x = r.variable sage: intersect([x(1),x(2)+1],[x(1),x(2)]) diff --git a/src/sage/rings/polynomial/pbori/intpolys.py b/src/sage/rings/polynomial/pbori/intpolys.py index 46fee86ddf3..9d000a566f4 100644 --- a/src/sage/rings/polynomial/pbori/intpolys.py +++ b/src/sage/rings/polynomial/pbori/intpolys.py @@ -36,7 +36,7 @@ def __add__(self, other): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS sage: p=IntegerPolynomial(x(1)) sage: p diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index ca6b5138a1f..5135fe5033c 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -212,7 +212,7 @@ class RingMap(object): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) sage: mapping = RingMap(to_ring, from_ring) @@ -238,7 +238,7 @@ def __init__(self, to_ring, from_ring): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) sage: mapping = RingMap(to_ring, from_ring) @@ -277,7 +277,7 @@ def __call__(self, poly): TESTS:: - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) sage: mapping = RingMap(to_ring, from_ring) @@ -290,7 +290,7 @@ def invert(self, poly): r""" Inverted map to initial ring. - sage: from sage.rings.polynomial.pbori.brial.frontend import * + sage: from sage.rings.polynomial.pbori.frontend import * sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) sage: mapping = RingMap(to_ring, from_ring) diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index dd537264c11..b15efac71ee 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -656,7 +656,7 @@ def normal_form(poly, ideal, reduced=True): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import declare_ring, normal_form + sage: from sage.rings.polynomial.pbori import declare_ring, normal_form sage: r=declare_ring(['x','y'], globals()) sage: normal_form(x+y, [y],reduced=True) x diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 5874fd57815..07f635448d1 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -45,7 +45,7 @@ def to_fast_pickable(l): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x=r.variable sage: to_fast_pickable([Polynomial(1, r)]) @@ -121,7 +121,7 @@ def from_fast_pickable(l, r): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x = r.variable sage: from_fast_pickable([[1], []], r) @@ -275,7 +275,7 @@ def groebner_basis_first_finished(I, *l): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial.PyPolyBoRi import Ring + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] sage: #groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 78ec68ef798..8aff5562449 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -157,7 +157,7 @@ Access to the original PolyBoRi interface The re-implementation PolyBoRi's native wrapper is available to the user too:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: declare_ring([Block('x',2),Block('y',3)],globals()) Boolean PolynomialRing in x0, x1, y0, y1, y2 sage: r @@ -1511,7 +1511,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): ALGORITHM: Calls ``interpolate_smallest_lex`` as described in the PolyBoRi tutorial. """ - #from sage.rings.polynomial.pbori.brial.interpolate import interpolate_smallest_lex + #from sage.rings.polynomial.pbori.interpolate import interpolate_smallest_lex from sage.misc.misc_c import prod n = self.ngens() x = self.gens() @@ -1837,7 +1837,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: M @@ -1862,7 +1862,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: B. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(B) sage: M @@ -1886,7 +1886,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: M # indirect doctest @@ -1900,7 +1900,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: {M:1} # indirect doctest @@ -1914,7 +1914,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P = BooleanPolynomialRing(100, 'x') sage: M = BooleanMonomialMonoid(P) sage: M.ngens() @@ -1932,7 +1932,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.gen(0) @@ -1959,7 +1959,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.gens() @@ -1973,7 +1973,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.get_action(ZZ) # indirect doctest @@ -1993,7 +1993,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x_monom = M(x); x_monom @@ -2004,7 +2004,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): Convert elements from :class:`BooleanMonomialMonoid` where the generators of self include the generators of the other monoid:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: N = BooleanMonomialMonoid(R) sage: m = M(N(y*z)); m @@ -2014,7 +2014,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: N = BooleanMonomialMonoid(R) sage: M(N(y)) @@ -2024,7 +2024,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): ... ValueError: cannot convert monomial t to MonomialMonoid of Boolean PolynomialRing in x, y, z: name t not defined - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(4) sage: N = BooleanMonomialMonoid(R) sage: M(N(x*y*z)) @@ -2061,7 +2061,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x_monom = M(x); x_monom @@ -2200,7 +2200,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid, BooleanMonomial + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid, BooleanMonomial sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: BooleanMonomial(M) @@ -2215,7 +2215,7 @@ cdef class BooleanMonomial(MonoidElement): """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid, BooleanMonomial + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid, BooleanMonomial sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: BooleanMonomial(M) @@ -2235,7 +2235,7 @@ cdef class BooleanMonomial(MonoidElement): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(R) sage: t = M.0*M.1 @@ -2251,7 +2251,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x) < M(y) @@ -2427,7 +2427,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*y).deg() @@ -2455,7 +2455,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*y).degree() @@ -2560,7 +2560,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: len(M(x*y)) @@ -2574,7 +2574,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: list(M(x*z)) # indirect doctest @@ -2588,7 +2588,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*z).variables() # indirect doctest @@ -2602,7 +2602,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: list(M(x*z).iterindex()) @@ -2616,7 +2616,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y); z=M(z) @@ -2642,7 +2642,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y) @@ -2686,7 +2686,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y) @@ -2738,7 +2738,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleSet + sage: from sage.rings.polynomial.pbori import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -2908,7 +2908,7 @@ cdef class BooleanPolynomial(MPolynomial): TESTS:: - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomial + sage: from sage.rings.polynomial.pbori import BooleanPolynomial sage: B. = BooleanPolynomialRing(3) sage: BooleanPolynomial(B) 0 @@ -4118,7 +4118,7 @@ cdef class BooleanPolynomial(MPolynomial): sage: loads(dumps(a)) == a True """ - from sage.rings.polynomial.pbori.brial.parallel import _encode_polynomial + from sage.rings.polynomial.pbori.parallel import _encode_polynomial return unpickle_BooleanPolynomial0, (self._parent, _encode_polynomial(self)) @@ -4345,7 +4345,7 @@ cdef class BooleanPolynomial(MPolynomial): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleSet + sage: from sage.rings.polynomial.pbori import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -4706,7 +4706,7 @@ cdef class BooleanPolynomial(MPolynomial): ... TypeError: argument must be a BooleanPolynomial. """ - from sage.rings.polynomial.pbori.brial import red_tail + from sage.rings.polynomial.pbori import red_tail if not I: return self if isinstance(I, BooleanPolynomialIdeal): @@ -4732,7 +4732,7 @@ cdef class PolynomialConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialConstruct().lead(a) a @@ -4746,7 +4746,7 @@ cdef class PolynomialConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialConstruct()(1, B) 1 @@ -4783,7 +4783,7 @@ cdef class MonomialConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: MonomialConstruct()(B) 1 @@ -4820,7 +4820,7 @@ cdef class VariableConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: VariableConstruct()(B) a @@ -5093,7 +5093,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): [x*z, x*y + y*z + y] """ - from sage.rings.polynomial.pbori.brial.gbcore import groebner_basis + from sage.rings.polynomial.pbori.gbcore import groebner_basis if self.ring().term_order()[0].name() == "degrevlex": # PolyBoRi's groebner_basis assumes increasing indices @@ -5190,7 +5190,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): sage: I.reduce(gb[0]*B.gen(1)) 0 """ - from sage.rings.polynomial.pbori.brial import red_tail + from sage.rings.polynomial.pbori import red_tail try: g = self.__gb except AttributeError: @@ -5318,7 +5318,7 @@ cdef class BooleSet: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleSet + sage: from sage.rings.polynomial.pbori import BooleSet sage: B. = BooleanPolynomialRing(4) sage: BS = BooleSet(a.set()) sage: BS @@ -5328,7 +5328,7 @@ cdef class BooleSet: sage: BS {{a,b}, {c}, {}} - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: BooleSet([Monomial(B)]) {{}} @@ -5375,7 +5375,7 @@ cdef class BooleSet: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleSet + sage: from sage.rings.polynomial.pbori import BooleSet sage: B. = BooleanPolynomialRing(4) sage: BS = BooleSet(B) sage: repr(BS) # indirect doctest @@ -5427,7 +5427,7 @@ cdef class BooleSet: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleSet + sage: from sage.rings.polynomial.pbori import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -6033,7 +6033,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6054,7 +6054,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6077,7 +6077,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: list(iter(v)) @@ -6090,7 +6090,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector() sage: len(v) @@ -6106,7 +6106,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6136,7 +6136,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6168,7 +6168,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: v = BooleanPolynomialVector() sage: for i in range(5): ....: v.append(B.random_element()) @@ -6231,7 +6231,7 @@ cdef class ReductionStrategy: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: del red @@ -6249,7 +6249,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x) @@ -6279,7 +6279,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -6303,7 +6303,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -6328,7 +6328,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6350,7 +6350,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(a*b + c + 1) @@ -6374,7 +6374,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(a*b + c + 1) @@ -6414,7 +6414,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6476,7 +6476,7 @@ cdef class ReductionStrategy: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6513,7 +6513,7 @@ cdef class FGLMStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: x > y > z True @@ -6561,7 +6561,7 @@ cdef class FGLMStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: ideal = BooleanPolynomialVector([x+z, y+z]) sage: list(ideal) @@ -6593,7 +6593,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: B. = BooleanPolynomialRing() sage: G = GroebnerStrategy(B) sage: H = GroebnerStrategy(G) @@ -6624,7 +6624,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_generator(a + b) @@ -6651,7 +6651,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_generator(a + b) @@ -6679,7 +6679,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_as_you_wish(a + b) @@ -6738,7 +6738,7 @@ cdef class GroebnerStrategy: spanned by the generators but not in the set of generators:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6752,7 +6752,7 @@ cdef class GroebnerStrategy: Still, we have that:: - sage: from sage.rings.polynomial.pbori.brial import groebner_basis + sage: from sage.rings.polynomial.pbori import groebner_basis sage: groebner_basis(gb) [1] """ @@ -6769,7 +6769,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6779,7 +6779,7 @@ cdef class GroebnerStrategy: sage: gb.add_generator(a*b + b + c*e + e + 1) sage: gb.add_generator(a + b + c*d + c*e + 1) - sage: from sage.rings.polynomial.pbori.brial import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector sage: V= BooleanPolynomialVector([b*d, a*b]) sage: list(gb.faugere_step_dense(V)) [b + c*e + e + 1, c + d*f + e + f] @@ -6845,7 +6845,7 @@ cdef class GroebnerStrategy: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_as_you_wish(a + b) @@ -6878,7 +6878,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6889,7 +6889,7 @@ cdef class GroebnerStrategy: sage: gb.variable_has_value(0) False - sage: from sage.rings.polynomial.pbori.brial import groebner_basis + sage: from sage.rings.polynomial.pbori import groebner_basis sage: g = groebner_basis(gb) sage: list(g) [a, b + 1, c + 1, d, e + 1, f] @@ -6918,7 +6918,7 @@ cdef class GroebnerStrategy: sage: I = B.ideal([B(f) for f in I.gens()]) sage: gb = I.groebner_basis() - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: G = GroebnerStrategy(B) sage: _ = [G.add_generator(f) for f in gb] @@ -6953,7 +6953,7 @@ cdef class GroebnerStrategy: sage: B. = BooleanPolynomialRing() sage: f = B.random_element() sage: g = B.random_element() - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: strat = GroebnerStrategy(B) sage: strat.add_generator(f) sage: strat.add_generator(g) @@ -6973,7 +6973,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import GroebnerStrategy + sage: from sage.rings.polynomial.pbori import GroebnerStrategy sage: G = GroebnerStrategy(B) sage: G.add_as_you_wish(a) @@ -7057,7 +7057,7 @@ cdef class BooleanMulAction(Action): """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y); z=M(z) @@ -7114,7 +7114,7 @@ def add_up_polynomials(BooleanPolynomialVector v, BooleanPolynomial init): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: v = BooleanPolynomialVector() sage: l = [B.random_element() for _ in range(5)] @@ -7143,7 +7143,7 @@ def red_tail(ReductionStrategy s, BooleanPolynomial p): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -7165,7 +7165,7 @@ def map_every_x_to_x_plus_one(BooleanPolynomial p): sage: B. = BooleanPolynomialRing(3) sage: f = a*b + z + 1; f a*b + z + 1 - sage: from sage.rings.polynomial.pbori.brial import map_every_x_to_x_plus_one + sage: from sage.rings.polynomial.pbori import map_every_x_to_x_plus_one sage: map_every_x_to_x_plus_one(f) a*b + a + b + z + 1 sage: f(a+1,b+1,z+1) @@ -7201,7 +7201,7 @@ def zeros(pol, BooleSet s): This encodes the points (1,1,1,0), (1,1,0,0), (0,0,1,1) and (0,1,1,0). But of these only (1,1,0,0) evaluates to zero.:: - sage: from sage.rings.polynomial.pbori.brial import zeros + sage: from sage.rings.polynomial.pbori import zeros sage: zeros(f,s) {{a,b}} @@ -7236,7 +7236,7 @@ def interpolate(zero, one): sage: B = BooleanPolynomialRing(4,"x0,x1,x2,x3") sage: x = B.gen - sage: from sage.rings.polynomial.pbori.brial.interpolate import * + sage: from sage.rings.polynomial.pbori.interpolate import * sage: V=(x(0)+x(1)+x(2)+x(3)+1).set() sage: V @@ -7301,7 +7301,7 @@ def interpolate_smallest_lex(zero, one): sage: B = BooleanPolynomialRing(4,"x0,x1,x2,x3") sage: x = B.gen - sage: from sage.rings.polynomial.pbori.brial.interpolate import * + sage: from sage.rings.polynomial.pbori.interpolate import * sage: V=(x(0)+x(1)+x(2)+x(3)+1).set() We take V = {e0,e1,e2,e3,0}, where ei describes the i-th unit @@ -7389,7 +7389,7 @@ def ll_red_nf_redsb(p, BooleSet reductors): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import ll_red_nf_redsb + sage: from sage.rings.polynomial.pbori import ll_red_nf_redsb sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7431,7 +7431,7 @@ def ll_red_nf_noredsb(BooleanPolynomial p, BooleSet reductors): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import ll_red_nf_noredsb + sage: from sage.rings.polynomial.pbori import ll_red_nf_noredsb sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7464,7 +7464,7 @@ def ll_red_nf_noredsb_single_recursive_call(BooleanPolynomial p, BooleSet reduct EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import ll_red_nf_noredsb_single_recursive_call + sage: from sage.rings.polynomial.pbori import ll_red_nf_noredsb_single_recursive_call sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7505,7 +7505,7 @@ def if_then_else(root, a, b): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import if_then_else + sage: from sage.rings.polynomial.pbori import if_then_else sage: B = BooleanPolynomialRing(6,'x') sage: x0,x1,x2,x3,x4,x5 = B.gens() sage: f0 = x2*x3+x3 @@ -7584,7 +7584,7 @@ def top_index(s): EXAMPLES:: sage: B. = BooleanPolynomialRing(3) - sage: from sage.rings.polynomial.pbori.brial import top_index + sage: from sage.rings.polynomial.pbori import top_index sage: top_index(x.lm()) 0 sage: top_index(y*z) @@ -7691,7 +7691,7 @@ def gauss_on_polys(inp): EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: l = [B.random_element() for _ in range(B.ngens())] sage: A,v = Sequence(l,B).coefficient_matrix() sage: A @@ -7734,7 +7734,7 @@ def substitute_variables(BooleanPolynomialRing parent, vec, BooleanPolynomial po sage: B. = BooleanPolynomialRing() sage: f = a*b + c + 1 - sage: from sage.rings.polynomial.pbori.brial import substitute_variables + sage: from sage.rings.polynomial.pbori import substitute_variables sage: substitute_variables(B, [a,b,c],f) a*b + c + 1 sage: substitute_variables(B, [a+1,b,c],f) @@ -7750,7 +7750,7 @@ def substitute_variables(BooleanPolynomialRing parent, vec, BooleanPolynomial po sage: f = a*b + c + 1 sage: B. = BooleanPolynomialRing(order='deglex') - sage: from sage.rings.polynomial.pbori.brial import substitute_variables + sage: from sage.rings.polynomial.pbori import substitute_variables sage: substitute_variables(B, [x,y,z], f) * w w*x*y + w*z + w @@ -7772,7 +7772,7 @@ def set_random_seed(seed): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import random_set, set_random_seed + sage: from sage.rings.polynomial.pbori import random_set, set_random_seed sage: B. = BooleanPolynomialRing() sage: (a*b*c*d).lm() a*b*c*d @@ -7798,7 +7798,7 @@ def random_set(BooleanMonomial variables, length): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import random_set, set_random_seed + sage: from sage.rings.polynomial.pbori import random_set, set_random_seed sage: B. = BooleanPolynomialRing() sage: (a*b*c*d).lm() a*b*c*d @@ -7815,7 +7815,7 @@ def easy_linear_factors(BooleanPolynomial p): return new_BPV_from_PBPolyVector(p._parent, pb_easy_linear_factors(p._pbpoly)) -# todo: merge with pickling from sage.rings.polynomial.pbori.brial.parallel +# todo: merge with pickling from sage.rings.polynomial.pbori.parallel def unpickle_BooleanPolynomial(ring, string): """ Unpickle boolean polynomials @@ -7830,7 +7830,7 @@ def unpickle_BooleanPolynomial(ring, string): return ring(eval(string,ring.gens_dict())) -# todo: merge with pickling from sage.rings.polynomial.pbori.brial.parallel +# todo: merge with pickling from sage.rings.polynomial.pbori.parallel def unpickle_BooleanPolynomial0(ring, l): """ Unpickle boolean polynomials @@ -7842,11 +7842,11 @@ def unpickle_BooleanPolynomial0(ring, l): sage: loads(dumps(a+b)) == a+b # indirect doctest True """ - from sage.rings.polynomial.pbori.brial.parallel import _decode_polynomial + from sage.rings.polynomial.pbori.parallel import _decode_polynomial return _decode_polynomial(l) -# todo: merge with pickling from sage.rings.polynomial.pbori.brial.parallel +# todo: merge with pickling from sage.rings.polynomial.pbori.parallel def unpickle_BooleanPolynomialRing(n, names, order): """ Unpickle boolean polynomial rings. @@ -7873,7 +7873,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: [BooleConstant(i) for i in range(5)] [0, 1, 0, 1, 0] """ @@ -7883,7 +7883,7 @@ cdef class BooleConstant: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: repr((BooleConstant(0),BooleConstant(1))) # indirect doctest '(0, 1)' @@ -7898,7 +7898,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: BooleConstant(0).deg() -1 sage: BooleConstant(1).deg() @@ -7912,7 +7912,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: BooleConstant(0).variables() () sage: BooleConstant(1).variables() @@ -7926,7 +7926,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: BooleConstant(0).is_one() False sage: BooleConstant(1).is_one() @@ -7940,7 +7940,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: BooleConstant(1).is_zero() False sage: BooleConstant(0).is_zero() @@ -7954,7 +7954,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: BooleConstant(1).is_constant() True sage: BooleConstant(0).is_constant() @@ -7968,7 +7968,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import BooleConstant + sage: from sage.rings.polynomial.pbori import BooleConstant sage: BooleConstant(1).has_constant_part() True sage: BooleConstant(0).has_constant_part() @@ -8017,7 +8017,7 @@ cdef class VariableFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: fac = VariableFactory() sage: fac = VariableFactory(B) @@ -8033,7 +8033,7 @@ cdef class VariableFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: VariableFactory()(B) a @@ -8066,7 +8066,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: fac = MonomialFactory() sage: fac = MonomialFactory(B) @@ -8079,7 +8079,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: MonomialFactory()(B) 1 @@ -8100,7 +8100,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: MonomialFactory()(B) 1 @@ -8145,7 +8145,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: fac = PolynomialFactory() @@ -8161,7 +8161,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialFactory().lead(a) a @@ -8175,7 +8175,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialFactory()(1, B) 1 diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index 27d20cde542..f87b6e281af 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -40,7 +40,7 @@ def sparse_random_system(ring, number_of_polynomials, TESTS:: - sage: from sage.rings.polynomial.pbori.brial import * + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(10) sage: s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) sage: [p.deg() for p in s] diff --git a/src/sage/sat/converters/__init__.py b/src/sage/sat/converters/__init__.py index 01ac16511ae..d0c8f81398e 100644 --- a/src/sage/sat/converters/__init__.py +++ b/src/sage/sat/converters/__init__.py @@ -1,3 +1,3 @@ from __future__ import absolute_import from .anf2cnf import ANF2CNFConverter -from sage.rings.polynomial.pbori.brial.cnf import CNFEncoder as PolyBoRiCNFEncoder +from sage.rings.polynomial.pbori.cnf import CNFEncoder as PolyBoRiCNFEncoder From 408824d44f039c51ba2ab8c9552bf2a0f8cfedcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 12:24:42 +1200 Subject: [PATCH 246/379] Fix building of the pbori interface documentation. No "brial" documentation is buiilt as far as I can tell. --- src/doc/en/reference/polynomial_rings/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index 4a30310f740..bbdf14685ca 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -67,6 +67,6 @@ Boolean Polynomials .. toctree:: :maxdepth: 1 - sage/rings/polynomial/pbori + sage/rings/polynomial/pbori/pbori .. include:: ../footer.txt From 443a3a8d5d3ba3b05db9312f33788d845836808d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 12:48:41 +1200 Subject: [PATCH 247/379] Remove addition.py - nothing from this file is used and tests failures point to the file being obsolote. --- src/sage/rings/polynomial/pbori/addition.py | 163 -------------------- 1 file changed, 163 deletions(-) delete mode 100644 src/sage/rings/polynomial/pbori/addition.py diff --git a/src/sage/rings/polynomial/pbori/addition.py b/src/sage/rings/polynomial/pbori/addition.py deleted file mode 100644 index 51433521eaa..00000000000 --- a/src/sage/rings/polynomial/pbori/addition.py +++ /dev/null @@ -1,163 +0,0 @@ -from .PyPolyBoRi import Polynomial, BooleSet, BooleConstant -from .partial import PartialFunction -from .specialsets import all_monomials_of_degree_d, power_set -from .ll import ll_encode, ll_red_nf_redsb - - -def add_bits_old(bits): - r""" - Adds n bits - - TESTS:: - - sage: from sage.rings.polynomial.pbori import * - sage: r=Ring(10) - sage: add_bits_old([r.variable(i) for i in xrange(3)]) - [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] - sage: add_bits_old([r.variable(i) for i in xrange(4)]) - [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3)] - """ - bits = list(bits) - n = len(bits) - deg_d_monomials = [Polynomial(all_monomials_of_degree_d(i, bits)) for i in - range(n + 1)] - full = power_set(bits) - bits_expr = [] # [sum(bits)] - step = 0 - while n > 2 ** step: - to_one = sum([deg_d_monomials[i] for i in range(n + 1) if i & 2 ** - step]) - to_one = Polynomial(to_one) - fun = PartialFunction(ones=to_one, zeros=full.diff(to_one)) - poly = fun.interpolate_smallest_lex() - bits_expr.append(poly) - step = step + 1 - return bits_expr - - -def add_bits(bits): - r""" - Adds n bit variables, by Lucas theorem - - TESTS:: - - sage: from sage.rings.polynomial.pbori import * - sage: r=Ring(10) - sage: add_bits([r.variable(i) for i in xrange(3)]) - [x(0) + x(1) + x(2), x(0)*x(1) + x(0)*x(2) + x(1)*x(2)] - sage: add_bits([r.variable(i) for i in xrange(4)]) - [x(0) + x(1) + x(2) + x(3), x(0)*x(1) + x(0)*x(2) + x(0)*x(3) + x(1)*x(2) + x(1)*x(3) + x(2)*x(3), x(0)*x(1)*x(2)*x(3)] - sage: add_bits([r.variable(0)]) - [x(0)] - """ - bits = list(bits) - if len(bits) < 2: - return bits - n = len(bits) - - bits_expr = [] # [sum(bits)] - step = 0 - while n >= 2 ** step: - bits_expr.append(Polynomial(all_monomials_of_degree_d(2 ** step, bits) - )) - step = step + 1 - return bits_expr - - -def add_bit_expressions(bit_expressions): - r""" - Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. - - TESTS:: - - sage: from sage.rings.polynomial.pbori import * - sage: r=Ring(20) - sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) - [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] - sage: add_bit_expressions([r.variable(i) for i in xrange(10,13)]) - [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] - sage: add_bit_expressions([r.variable(11), r.variable(11)]) - [0, x(11)] - sage: add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) - [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] - """ - - bit_variables = [] - if bit_expressions: - ring = bit_expressions[0].ring() - bit_variables = [ring.variable(i) for i in range(len(bit_expressions) - )] - for expr in bit_expressions: - assert BooleSet(expr).navigation().value() >= len(bit_variables) - mapping = ll_encode([b + expr for (b, expr) in zip(bit_variables, - bit_expressions)]) - return [ll_red_nf_redsb(p, mapping) for p in add_bits(bit_variables)] - - -def add_words(words): - r""" - Adds n words, this words are supposed to consists of list of their bits. - - TESTS:: - - sage: from sage.rings.polynomial.pbori import * - sage: r=Ring(1000) - sage: add_words([[r.variable(100+i*3+j) for i in xrange(2)] for j in xrange(3)]) - [x(100) + x(101) + x(102), x(100)*x(101) + x(100)*x(102) + x(101)*x(102) + x(103) + x(104) + x(105), x(100)*x(101)*x(103) + x(100)*x(101)*x(104) + x(100)*x(101)*x(105) + x(100)*x(102)*x(103) + x(100)*x(102)*x(104) + x(100)*x(102)*x(105) + x(101)*x(102)*x(103) + x(101)*x(102)*x(104) + x(101)*x(102)*x(105) + x(103)*x(104) + x(103)*x(105) + x(104)*x(105), x(100)*x(101)*x(103)*x(104)*x(105) + x(100)*x(102)*x(103)*x(104)*x(105) + x(101)*x(102)*x(103)*x(104)*x(105)] - sage: res=add_words([[r.variable(100+i*9+j) for i in xrange(4)] for j in xrange(9)]) - sage: [len(p) for p in res] - [9, 45, 495, 12870, 735462, 70285482, 1891358892, 6435] - sage: [p.deg() for p in res] - [1, 2, 4, 8, 12, 18, 25, 33] - sage: [p.n_nodes() for p in res] - [9, 25, 54, 100, 153, 211, 249, 100] - """ - - max_word_length = max((len(w) for w in words)) - res = [] - while len(words) > 0: - words = [w for w in words if len(w) > 0] - bits = add_bit_expressions([w[0] for w in words]) - words = [w[1:] for w in words] - if len(bits) > 0: - res.append(bits[0]) - words.append(bits[1:]) - return res - - -def multiply_by_addition(word_a, word_b): - r""" - Multiply two words - - TESTS:: - - sage: from sage.rings.polynomial.pbori import Ring - sage: r=Ring(1000) - sage: x = r.variable - sage: n=7 - sage: res=multiply_by_addition([x(200+2*i) for i in xrange(n)], [x(200+2*i+1) for i in xrange(n)]) - sage: [p.n_nodes() for p in res] - [2, 4, 7, 17, 38, 85, 222, 630, 1358, 1702, 1713, 1430, 875, 214, 0] - """ - word_a = list(word_a) - word_b = list(word_b) - summands = [] - if word_a: - zero = word_a[0].ring().zero() - elif word_b: - zero = word_b[0].ring().zero() - else: - zero = BooleConstant(0) - - for (i, a) in enumerate(word_a): - summands.append(i * [zero] + [a * b for b in word_b]) - - return add_words(summands) - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() From 167ef09e38e16eee05a7c278c47558772666e6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 12:50:57 +1200 Subject: [PATCH 248/379] migrate xrange (unsupported in python3) to range --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 4 ++-- src/sage/rings/polynomial/pbori/fglm.py | 2 +- src/sage/rings/polynomial/pbori/gbcore.py | 4 ++-- src/sage/rings/polynomial/pbori/pbori.pyx | 8 ++++---- src/sage/rings/polynomial/pbori/randompoly.py | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index cd472ccaf03..a5e1bf94b20 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -57,10 +57,10 @@ True sage: r = r.clone(names=["z17", "z7"]) - sage: [r.variable(idx) for idx in xrange(3)] + sage: [r.variable(idx) for idx in range(3)] [z17, z7, x2] sage: r = r.clone(names="abcde") - sage: [r.variable(idx) for idx in xrange(6)] + sage: [r.variable(idx) for idx in range(6)] [a, b, c, d, e, y2] """ diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index ab0ef25102f..210fe052e6a 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -35,7 +35,7 @@ def fglm(I, from_ring, to_ring): sage: r=declare_ring(['x','y','z'],dict()) sage: old_ring = r sage: new_ring = old_ring.clone(ordering=dp_asc) - sage: (x,y,z) = [old_ring.variable(i) for i in xrange(3)] + sage: (x,y,z) = [old_ring.variable(i) for i in range(3)] sage: ideal=[x+z, y+z]# lp Groebner basis sage: list(fglm(ideal, old_ring, new_ring)) [y + x, z + x] diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index 3bb8ab0e60b..4c1458624fe 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -357,8 +357,8 @@ def variety_size_from_gb(I): 6.0 sage: variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) 5.0 - sage: mons = [Monomial([r.variable(i) for i in xrange(100) if i!=j])\ - for j in xrange(100)] + sage: mons = [Monomial([r.variable(i) for i in range(100) if i!=j])\ + for j in range(100)] sage: variety_size_from_gb(mons) 1.2676506002282294e+30 """ diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 8aff5562449..8da8c3b7de6 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -1518,14 +1518,14 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): if isinstance(zeros, BooleSet): z = zeros else: - z = sum([prod([x[i] for i in xrange(n) if v[i]], + z = sum([prod([x[i] for i in range(n) if v[i]], self.one()) for v in zeros], self.zero()) z = z.set() if isinstance(ones, BooleSet): o = ones else: - o = sum([prod([x[i] for i in xrange(n) if v[i]], + o = sum([prod([x[i] for i in range(n) if v[i]], self.one()) for v in ones], self.zero()) o = o.set() @@ -1798,7 +1798,7 @@ def get_var_mapping(ring, other): """ my_names = list(ring._names) # we need .index(.) if isinstance(other, (Parent, BooleanMonomialMonoid)): - indices = list(xrange(other.ngens())) + indices = list(range(other.ngens())) ovar_names = other._names else: ovar_names = other.parent().variable_names() @@ -1965,7 +1965,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): sage: M.gens() (x, y, z) """ - return tuple(self.gen(i) for i in xrange(self.ngens())) + return tuple(self.gen(i) for i in range(self.ngens())) def _get_action_(self, S, op, bint self_on_left): """ diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index f87b6e281af..5c5f5de473b 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -80,14 +80,14 @@ def sparse_random_system_data_file_content( TESTS:: sage: sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) - declare_ring(['x'+str(i) for in xrange(10)])\nideal=\\\n[...]\n\n + declare_ring(['x'+str(i) for in range(10)])\nideal=\\\n[...]\n\n """ dummy_dict = dict() r = declare_ring(['x' + str(i) for i in range(number_of_variables)], dummy_dict) polynomials = sparse_random_system(r, **kwds) polynomials = pformat(polynomials) - res = "declare_ring(['x'+str(i) for in xrange(%s)])\nideal=\\\n%s\n\n" % ( + res = "declare_ring(['x'+str(i) for in range(%s)])\nideal=\\\n%s\n\n" % ( number_of_variables, polynomials) return res From afdbb9d8f84b2cec5f4c885b3a19577c2d04b695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 13:06:09 +1200 Subject: [PATCH 249/379] fix doctests in blocks.py --- src/sage/rings/polynomial/pbori/blocks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py index a2fb886b956..5a9749db5d0 100644 --- a/src/sage/rings/polynomial/pbori/blocks.py +++ b/src/sage/rings/polynomial/pbori/blocks.py @@ -367,7 +367,9 @@ def declare_ring(blocks, context=None): EXAMPLES:: + sage: from sage.rings.polynomial.pbori import * sage: declare_ring([Block("x",10),Block("y",5)],globals()) + Boolean PolynomialRing in x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, y0, y1, y2, y3, y4 gives a ring with x(0..9),y(0..4) and registers the ring as r, and the variable blocks x and y in the context dictionary globals(), which consists of the global From ec4cddb6f49b0130c269beba58404bd08f0657cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 13:06:55 +1200 Subject: [PATCH 250/379] remove check_claims.py which appears to be a standalone script. --- .../rings/polynomial/pbori/check_claims.py | 205 ------------------ 1 file changed, 205 deletions(-) delete mode 100644 src/sage/rings/polynomial/pbori/check_claims.py diff --git a/src/sage/rings/polynomial/pbori/check_claims.py b/src/sage/rings/polynomial/pbori/check_claims.py deleted file mode 100644 index afa11b9b0e5..00000000000 --- a/src/sage/rings/polynomial/pbori/check_claims.py +++ /dev/null @@ -1,205 +0,0 @@ -# encoding: utf-8 -r""" -check_claims.py - -Created by Michael Brickenstein on 2007-03-05. -Copyright (c) 2007 The PolyBoRi Team. -""" -from __future__ import print_function - -import sys -from optparse import OptionParser -#if __name__ == "__main__": -# import pathadjuster -from .PyPolyBoRi import Polynomial, Monomial, BooleConstant, BooleSet -from .PyPolyBoRi import recursively_insert -from .gbrefs import my_import, load_data, clean_data, load_file -from .blocks import IfThen -from copy import copy -from .ll import ll_encode, ll_red_nf_noredsb, ll_red_nf_redsb - - -def find_one(p, res=None): - def zero_nav(n): - return n.constant() and (not n.terminal_one()) - try: - p = p.navigation() - except AttributeError: - pass - if res is None: - res = dict() - if zero_nav(p): - raise ValueError - if p.terminal_one(): - return res - else_branch = p.else_branch() - if zero_nav(else_branch): - res[Monomial(Variable(p.value()))] = 1 - find_one(p.then_branch(), res) - else: - res[Monomial(Variable(p.value()))] = 0 - find_one(else_branch, res) - return res - -parser = OptionParser() -NF3 = "nf3" -LINEAR_LEAD_NOREDSB = "ll" -parser.add_option("--method", - action="store", dest="method", type="choice", - choices=["nf3", "linear-lead-redsb", LINEAR_LEAD_NOREDSB], - default="linear-lead-redsb", - help="select method") - - -def my_red_nf(p, strat): - if p.is_zero(): - return p - hr = nf3(strat.reduction_strategy, p, p.lead()) - if hr.is_zero(): - return hr - return red_tail(strat, hr) - - -def gen_strat(polys): - polys = [Polynomial(p) for p in polys] - polys = [p for p in polys if not p.is_zero()] - assert len(set([p.lead() for p in polys])) == len(polys) - assert polys - strat = GroebnerStrategy(polys[0].ring()) - for p in polys: - print("Adding") - strat.add_generator(p) - print("finished") - return strat - - -def logicaland(l): - res = BooleConstant(0) - for p in l: - res = 1 + (res + 1) * (p + 1) - return res - - -def logicalor(l): - res = BooleConstant(1) - for p in l: - res = res * p - return res - - -def proof(ifthen, strat): - ip = ifthen.ifpart - it = ifthen.thenpart - print("proofing:", ifthen) - c = logicalor([1 + logicaland(ip), logicaland(it)]) - if c.is_zero(): - print("TRUE (trivial)") - return - else: - c = nf3(strat.reduction_strategy, c, c.lead()) - if c.is_zero(): - print("TRUE") - return - else: - print("FALSE") - - -def proofll(ifthen, reductors, redsb=True, prot=True): - - if prot and (not ifthen.supposedToBeValid): - print("THIS THEOREM IS NOT SUPPOSED TO BE VALID") - ip_pre = ifthen.ifpart - ip = [] - - for p in ip_pre: - p = Polynomial(p) - if p.is_zero(): - continue - li = list(p.lead().variables()) - if len(li) == 1 and (not (li[0] in list(Polynomial(reductors).lead(). - variables()))): - assert not Polynomial(reductors).is_zero() - lead_index = li[0] - if redsb: - p = ll_red_nf_redsb(p, reductors) - reductors = ll_red_nf_redsb(Polynomial(reductors), BooleSet(p. - set())) - - p_nav = p.navigation() - reductors = recursively_insert(p_nav.else_branch(), p_nav.value(), - reductors) - else: - ip.append(p) - it = ifthen.thenpart - if prot: - print("proofing:", ifthen) - ip = logicaland(ip) - for c in it: - if prot: - print("proofing part:", c) - c = logicalor([BooleConstant(1) + ip, c]) - - if c.is_zero(): - if prot: - print("TRUE (trivial)") - return True - else: - c_orig = c - if redsb: - c = ll_red_nf_redsb(c, reductors) - else: - c = ll_red_nf_noredsb(c, reductors) - if c.is_zero(): - if prot: - print("TRUE") - return True - else: - if prot: - print("FAILED") - print("can construct COUNTER EXAMPLE with:", find_one(c)) - return False - - -def to_if_then(p): - if isinstance(p, IfThen): - return p - else: - return IfThen([], [p]) - - -def main(argv=None): - (opts, args) = parser.parse_args() - mydata = load_file(args[0]) - claims = mydata.claims - if opts.method == NF3: - strat = gen_strat(mydata.ideal) - for c in claims: - proof(to_if_then(c), strat) - del strat - try: - del c - except NameError: - pass - else: - if opts.method == LINEAR_LEAD_NOREDSB: - reductors = ll_encode(mydata.ideal) - for c in claims: - proofll(to_if_then(c), reductors, redsb=False) - del reductors - try: - del c - except NameError: - pass - else: - reductors = ll_encode(mydata.ideal, reduce=True) - for c in claims: - proofll(to_if_then(c), reductors) - del reductors - try: - del c - except NameError: - pass - return 0 - -if __name__ == "__main__": - sys.exit(main()) From d53714fe9fead3bfe1485e21dfbe1f6f513998eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 13:08:43 +1200 Subject: [PATCH 251/379] remove another script --- src/sage/rings/polynomial/pbori/cluster.py | 119 --------------------- 1 file changed, 119 deletions(-) delete mode 100644 src/sage/rings/polynomial/pbori/cluster.py diff --git a/src/sage/rings/polynomial/pbori/cluster.py b/src/sage/rings/polynomial/pbori/cluster.py deleted file mode 100644 index 5d35766b3b2..00000000000 --- a/src/sage/rings/polynomial/pbori/cluster.py +++ /dev/null @@ -1,119 +0,0 @@ -# encoding: utf-8 -r""" -cluster.py - -Created by Michael Brickenstein on 2011-08-05. -Copyright 2011 The PolyBoRi Team. -""" - -import sys -import os -from .statistics import used_vars, used_vars_set -from .PyPolyBoRi import Variable - - -def main(): - pass - - -class ClusterAlgorithmFailed(Exception): - pass - - -class ClusterAlgorithm(object): - def __init__(self, ideal, determination_modifier=1): - if len(ideal) == 0: - raise ValueError('ideal generators list should be non empty') - - self.ideal = ideal - - self.determination_modifier = determination_modifier - - self.used_variables_ideal = used_vars_set(ideal) - - self.number_of_used_variables_in_ideal = len(self.used_variables_ideal) - self.used_variables_of_polynomial = dict( - [(p, set(p.vars_as_monomial().variables())) for p in ideal]) - self.variables_introduction_mapping = dict() - - self.cluster = set() - self.used_variables_cluster = set() - self.build_variables_usage() - self.initialize_variables_introduction_mapping() - - def build_variables_usage(self): - self.variables_usage = dict() - for (p, variables) in self.used_variables_of_polynomial.items(): - for v in variables: - self.variables_usage.setdefault(v, []).append(p) - - def initialize_variables_introduction_mapping(self): - self._build_variables_introduction_mapping() - - def _build_variables_introduction_mapping(self): - def var_set_to_tuple(s): - return tuple(sorted(s, key=Variable.index)) - self.variables_introduction_mapping.clear() - for (p, var_set) in self.used_variables_of_polynomial.items(): - if p in self.cluster: - continue - as_tuple = var_set_to_tuple(var_set.difference(self. - used_variables_cluster)) - self.variables_introduction_mapping.setdefault( - as_tuple, []).append(p) - - def adjust_variables_introduction_mapping(self, introduced_variables): - self._build_variables_introduction_mapping() - - def determined_enough(self): - return (self.number_of_used_variables_in_cluster + self. - determination_modifier <= len(self.cluster)) - - def find_cluster(self): - p = self.initial_choice() - self.cluster = set() - self.add_polynomial_to_cluster(p) - while not self.determined_enough(): - self.increase_cluster() - return list(self.cluster) - - def add_polynomial_to_cluster(self, p): - self.cluster.add(p) - self.used_variables_cluster = set(used_vars_set(self.cluster). - variables()) - self.number_of_used_variables_in_cluster = len(self. - used_variables_cluster) - self.adjust_variables_introduction_mapping(self. - used_variables_of_polynomial[p]) - - def initial_choice(self): - def max_key(entry): - (entry_variable, entry_polynomials) = entry - return len(entry_polynomials) - (variable, polynomials) = max(self.variables_usage.items(), - key=max_key) - - def min_key(p): - return len(self.used_variables_of_polynomial[p]) - return min(polynomials, key=min_key) - - def increase_cluster(self): - introduced_variables_possibilities = (list(self. - variables_introduction_mapping.keys())) - introduced_variables = min(introduced_variables_possibilities, key=len) - polynomials = self.variables_introduction_mapping[introduced_variables] - assert len(polynomials) > 0 - for p in polynomials: - self.add_polynomial_to_cluster(p) - if len(self.cluster) == len(self.ideal): - raise ClusterAlgorithmFailed - self.adjust_variables_introduction_mapping(introduced_variables) - - -def find_cluster(ideal): - algorithm = ClusterAlgorithm(ideal) - return algorithm.find_cluster() - - -if __name__ == '__main__': - main() From 9f4881d2a93b631383a3878f0c393f47e1f172ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Thu, 20 Aug 2020 03:31:14 +0200 Subject: [PATCH 252/379] #29483: Upgrade: gsl 2.6 --- build/pkgs/gsl/checksums.ini | 7 ++++--- build/pkgs/gsl/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/gsl/checksums.ini b/build/pkgs/gsl/checksums.ini index f1cb0a2e3ca..0d63c733eb6 100644 --- a/build/pkgs/gsl/checksums.ini +++ b/build/pkgs/gsl/checksums.ini @@ -1,4 +1,5 @@ tarball=gsl-VERSION.tar.gz -sha1=9b3a32ba4f4e79fc6164fdc62e56003ae6e991e5 -md5=52fcbe147ba413d78841084cb43c95fb -cksum=570181656 +sha1=9273164b6bdf60d0577518a1c1310eff6659e3dd +md5=bda73a3dd5ff2f30b5956764399db6e7 +cksum=1462047518 +upstream_url=https://ftp.gnu.org/gnu/gsl/gsl-VERSION.tar.gz diff --git a/build/pkgs/gsl/package-version.txt b/build/pkgs/gsl/package-version.txt index 95e3ba81920..5154b3f68e9 100644 --- a/build/pkgs/gsl/package-version.txt +++ b/build/pkgs/gsl/package-version.txt @@ -1 +1 @@ -2.5 +2.6 From 56d093fc63ec8117bf9fdbd1faf6a5fe979b6f7f Mon Sep 17 00:00:00 2001 From: Chase Meadors Date: Wed, 19 Aug 2020 20:58:39 -0600 Subject: [PATCH 253/379] Fix pyflakes failure Remove unused import --- src/sage/combinat/fully_commutative_elements.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index b90ef574e10..9584aec5dd1 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -32,7 +32,6 @@ from sage.categories.enumerated_sets import EnumeratedSets from sage.structure.unique_representation import UniqueRepresentation from .root_system.coxeter_matrix import CoxeterMatrix -from .root_system.cartan_type import CartanType from collections import deque from sage.combinat.posets.posets import Poset from sage.categories.coxeter_groups import CoxeterGroups From 72f8a6a90eba8c12b32aad723ee2d4f83c81067b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 16:20:08 +1200 Subject: [PATCH 254/379] Fix doctest and code in cnf.py --- src/sage/rings/polynomial/pbori/cnf.py | 44 +++++++++++--------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py index 74dc918eb2c..fe381bcff2f 100644 --- a/src/sage/rings/polynomial/pbori/cnf.py +++ b/src/sage/rings/polynomial/pbori/cnf.py @@ -20,9 +20,10 @@ def zero_blocks(self, f): sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) + sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder sage: e = CNFEncoder(r) sage: e.zero_blocks(r.variable(0)*r.variable(1)*r.variable(2)) - [{y: 0}, {z: 0}, {x: 0}] + [{z: 0}, {y: 0}, {x: 0}] """ f = Polynomial(f) variables = f.vars_as_monomial() @@ -87,16 +88,12 @@ def clauses(self, f): sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) + sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder sage: e = CNFEncoder(r) - sage: e.clauses(r.variable(0)*r.variable(1)*r.variable(2)) # doctest:+ELLIPSIS - [{...x: 0...}] - sage: e.clauses(r.variable(1)+r.variable(0)) # doctest:+ELLIPSIS - [{...x: 1...}, {...y: 1...}] - - sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(0)*r.variable(1)*r.variable(2))] - [[(z, 0), (y, 0), (x, 0)]] - sage: [sorted(c.iteritems()) for c in e.clauses(r.variable(1)+r.variable(0))] - [[(y, 1), (x, 0)], [(y, 0), (x, 1)]] + sage: sorted(e.clauses(r.variable(0)*r.variable(1)*r.variable(2)), key=lambda d: sorted(d.items())) + [{z: 0, y: 0, x: 0}] + sage: sorted(e.clauses(r.variable(Integer(1))+r.variable(Integer(0))), key=lambda d: sorted(d.items())) + [{y: 0, x: 1}, {y: 1, x: 0}] """ f_plus_one = f + 1 blocks = self.zero_blocks(f + 1) @@ -114,6 +111,7 @@ def polynomial_clauses(self, f): sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) + sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder sage: e = CNFEncoder(r) sage: e.polynomial_clauses(r.variable(0)*r.variable(1)*r.variable(2)) [x*y*z] @@ -137,7 +135,7 @@ def to_dimacs_index(self, v): return v.index() + 1 def dimacs_encode_clause(self, c): - def get_sign(val): + def get_sign(value): if value == 1: return 1 return -1 @@ -157,10 +155,11 @@ def dimacs_encode_polynomial(self, p): sage: from sage.rings.polynomial.pbori import * sage: d=dict() sage: r = declare_ring(["x", "y", "z"], d) + sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder sage: e = CNFEncoder(r) - sage: e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"]) - ['1 2 -3 0', '1 -2 3 0', '-1 -2 -3 0', '-1 2 3 0'] - """ + sage: sorted(e.dimacs_encode_polynomial(d["x"]+d["y"]+d["z"])) + ['-1 -2 -3 0', '-1 2 3 0', '1 -2 3 0', '1 2 -3 0'] + """ clauses = self.clauses(p) res = [] for c in clauses: @@ -174,13 +173,14 @@ def dimacs_cnf(self, polynomial_system): sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) + sage: from sage.rings.polynomial.pbori.cnf import CNFEncoder sage: e = CNFEncoder(r) sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0' sage: e.dimacs_cnf([r.variable(1)+r.variable(0)]) - 'c cnf generated by PolyBoRi\np cnf 3 2\n1 -2 0\n-1 2 0' + 'c cnf generated by PolyBoRi\np cnf 3 2\n-1 2 0\n1 -2 0' sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2), r.variable(1)+r.variable(0)]) - 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n-1 2 0\n1 -2 0' + 'c cnf generated by PolyBoRi\np cnf 3 3\n-1 -2 -3 0\n1 -2 0\n-1 2 0' """ clauses_list = [c for p in polynomial_system for c in self. dimacs_encode_polynomial(p)] @@ -204,6 +204,7 @@ def dimacs_encode_polynomial(self, p): sage: from sage.rings.polynomial.pbori import * sage: d=dict() sage: r = declare_ring(["x", "y", "z"], d) + sage: from sage.rings.polynomial.pbori.cnf import CryptoMiniSatEncoder sage: e = CryptoMiniSatEncoder(r) sage: p = d["x"]+d["y"]+d["z"] sage: p.deg() @@ -214,7 +215,7 @@ def dimacs_encode_polynomial(self, p): ['x1 2 3 0\nc g 1 x + y + z'] sage: e.dimacs_encode_polynomial(p+1) ['x1 2 -3 0\nc g 2 x + y + z + 1'] - """ + """ if p.deg() != 1 or len(p) <= 1: res = super(CryptoMiniSatEncoder, self).dimacs_encode_polynomial(p) else: @@ -240,6 +241,7 @@ def dimacs_cnf(self, polynomial_system): sage: from sage.rings.polynomial.pbori import * sage: r = declare_ring(["x", "y", "z"], dict()) + sage: from sage.rings.polynomial.pbori.cnf import CryptoMiniSatEncoder sage: e = CryptoMiniSatEncoder(r) sage: e.dimacs_cnf([r.variable(0)*r.variable(1)*r.variable(2)]) 'c cnf generated by PolyBoRi\np cnf 3 1\n-1 -2 -3 0\nc g 1 x*y*z\nc v 1 x\nc v 2 y\nc v 3 z' @@ -253,11 +255,3 @@ def dimacs_cnf(self, polynomial_system): res = res + "\n" + "\n".join(["c v %s %s" % (self.to_dimacs_index(v), v) for v in uv]) return res - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() From e25dd810b818d1543b66313d608ca33d93341b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 16:24:42 +1200 Subject: [PATCH 255/379] Fix pbori related doctests and code in multi_polynomial_sequence --- src/sage/rings/polynomial/multi_polynomial_sequence.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index af95524f3b7..5f03f95aac8 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1248,8 +1248,7 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc This is called "massaging" in [BCJ2007]_. """ - from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing - from sage.rings.polynomial.pbori import gauss_on_polys + from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing,gauss_on_polys from sage.rings.polynomial.pbori.ll import eliminate,ll_encode,ll_red_nf_redsb R = self.ring() @@ -1330,7 +1329,7 @@ def _groebner_strategy(self): sage: P. = BooleanPolynomialRing() sage: F = Sequence([x*y + z, y + z + 1]) sage: F._groebner_strategy() - + """ from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing R = self.ring() From 2c9b36f9f45a2ca8cc7c40671e5b560d2e7d6580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 17:02:45 +1200 Subject: [PATCH 256/379] Remove unused files --- src/sage/rings/polynomial/pbori/coding.py | 91 --- src/sage/rings/polynomial/pbori/context.py | 109 ---- .../pbori/general_boolean_polynomial.py | 616 ------------------ src/sage/rings/polynomial/pbori/intersect.py | 48 -- src/sage/rings/polynomial/pbori/intpolys.py | 90 --- src/sage/rings/polynomial/pbori/ncf.py | 41 -- src/sage/rings/polynomial/pbori/parsegat.py | 534 --------------- src/sage/rings/polynomial/pbori/partial.py | 51 -- src/sage/rings/polynomial/pbori/plot.py | 259 -------- src/sage/rings/polynomial/pbori/simplebb.py | 80 --- 10 files changed, 1919 deletions(-) delete mode 100644 src/sage/rings/polynomial/pbori/coding.py delete mode 100644 src/sage/rings/polynomial/pbori/context.py delete mode 100644 src/sage/rings/polynomial/pbori/general_boolean_polynomial.py delete mode 100644 src/sage/rings/polynomial/pbori/intersect.py delete mode 100644 src/sage/rings/polynomial/pbori/intpolys.py delete mode 100644 src/sage/rings/polynomial/pbori/ncf.py delete mode 100644 src/sage/rings/polynomial/pbori/parsegat.py delete mode 100644 src/sage/rings/polynomial/pbori/partial.py delete mode 100644 src/sage/rings/polynomial/pbori/plot.py delete mode 100644 src/sage/rings/polynomial/pbori/simplebb.py diff --git a/src/sage/rings/polynomial/pbori/coding.py b/src/sage/rings/polynomial/pbori/coding.py deleted file mode 100644 index bae1049a323..00000000000 --- a/src/sage/rings/polynomial/pbori/coding.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import print_function - -from .PyPolyBoRi import Polynomial -from .nf import symmGB_F2_C -from .ll import ll_encode - -try: - from itertools import ifilter as filter -except ImportError: - pass - - -class OccCounter(object): - def __init__(self): - self.impl = dict() - - def __getitem__(self, k): - try: - return self.impl[k] - except KeyError: - return 0 - - def increase(self, k): - try: - self.impl[k] = self.impl[k] + 1 - except KeyError: - self.impl[k] = 1 - - def uniques(self): - def filter_fun(k): - return self.impl[k] == 1 - return filter(filter_fun, self.impl.keys()) - - -def preprocess(I, prot=True): - def min_gb(I): - strat = symmGB_F2_C(I, opt_lazy=False, opt_exchange=False, prot=prot, - selection_size=10000, opt_red_tail=True) - return list(strat.minimalize_and_tail_reduce()) - I = [Polynomial(p) for p in I] - lin = [p for p in I if p.deg() == 1] - # lin_strat=symmGB_F2_C(lin, opt_lazy=False,opt_exchange=False,prot=prot,sele - # ction_size=10000,opt_red_tail=True) - lin = min_gb(lin) # list(lin_strat.minimalize_and_tail_reduce()) - for m in sorted([p.lead() for p in lin]): - print(m) - lin_ll = ll_encode(lin) - square = [p.lead() for p in I if p.deg() == 2 and len(p) == 1] - assert(len(lin) + len(square) == len(I)) - res = list(lin) - counter = OccCounter() - - def unique_index(s): - for idx in s: - if counter[idx] == 1: - return idx - never_come_here = False - assert never_come_here - - for m in square: - for idx in m: - counter.increase(idx) - systems = dict(((idx, []) for idx in counter.uniques())) - for m in square: - u_index = unique_index(m) - systems[u_index].append(ll_red_nf(m / Variable(u_index), lin_ll)) - rewritings = dict() - u_var = Variable(u) - u_im = ll_red_nf(u_var, lin_ll) - if not u_im.isConstant(): - if u_im != u_var: - r = u_im.navigation().value() - else: - pass - for u in counter.uniques(): - #print u - u_var = Variable(u) - u_im = ll_red_nf(u_var, lin_ll) - if not u_im.isConstant(): - if u_im != u_var: - r = u_im.navigation().value() - else: - pass - for u in systems.keys(): - u_var = Variable(u) - u_im = ll_red_nf(u_var, lin_ll) - res.extend([u_im * p for p in min_gb(systems[u])]) - #print [u_im*p for p in min_gb(systems[u])] - print("lin:", len(lin), "res:", len(res), "square:", len(square)) - res = [p for p in (Polynomial(p) for p in res) if not p.is_zero()] - return res diff --git a/src/sage/rings/polynomial/pbori/context.py b/src/sage/rings/polynomial/pbori/context.py deleted file mode 100644 index e61ed81a0ce..00000000000 --- a/src/sage/rings/polynomial/pbori/context.py +++ /dev/null @@ -1,109 +0,0 @@ -if __name__ == '__main__': - from sys import path as search_path - from os import path as file_path - search_path.append(file_path.join(file_path.dirname(__file__), '..')) - - -def _exists(): - r""" - PolyBoRi convention: checking optional components for prerequisites here - - TESTS:: - - sage: from sage.rings.polynomial.pbori.context import * - sage: _exists() - True - """ - from distutils.sysconfig import get_python_version - return float(get_python_version()) > 2.4 - -from .PyPolyBoRi import Ring, VariableFactory, MonomialFactory -from .PyPolyBoRi import PolynomialFactory, SetFactory -from .PyPolyBoRi import Variable, Monomial, Polynomial, BooleSet -import polybori - - -class FactoryContext(object): - r""" - Temporarily exchange the constructor of a given type with a compatible - callable object. It is useful together with the with statement. - - EXAMPLES:: - - sage: r = Ring(1000) - sage: from sage.rings.polynomial.pbori import Variable - sage: def var(idx): return Variable(idx, r) - sage: with FactoryContext(Variable, var): - ... print Variable(17) - x(17) - sage: try: - ... print Variable(17) - ... except: - ... print "caught expected exception" - caught expected exception - """ - - def __init__(self, original, factory): - self.original = original - self.factory = factory - - def __enter__(self): - self.fallback = self.original.__init__ - - def func(orig, *args): - try: - self.fallback(orig, self.factory(*args)) - except: - self.fallback(orig, *args) - - self.original.__init__ = func - return self - - def __exit__(self, type, value, traceback): - self.original.__init__ = self.fallback - return False - - -class RingContext(object): - r""" - Temporarily fix the ring for constructors of some ring-dependent types - like Variable and Monomial to a given ring. It is useful together with - the with statement. - - EXAMPLES:: - - sage: r = Ring(1000) - sage: from sage.rings.polynomial.pbori import Variable - sage: print Variable(17, r) - x(17) - sage: with RingContext(r): - ... print Variable(17), Monomial(), Polynomial(0), BooleSet() - x(17) 1 0 {} - sage: try: - ... print Variable(17) - ... except: - ... print "caught expected exception" - caught expected exception - """ - - def __init__(self, ring): - self.contexts = (FactoryContext(Variable, VariableFactory(ring)), - FactoryContext(Monomial, MonomialFactory(ring)), - FactoryContext(Polynomial, PolynomialFactory(ring)), - FactoryContext(BooleSet, SetFactory(ring))) - - def __enter__(self): - for elt in self.contexts: - elt.__enter__() - return self - - def __exit__(self, type, value, traceback): - result = False - for elt in reversed(self.contexts): - result = result or elt.__exit__(type, value, traceback) - return result - - -if __name__ == '__main__': - import doctest - doctest.testmod() diff --git a/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py b/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py deleted file mode 100644 index 0595e86df16..00000000000 --- a/src/sage/rings/polynomial/pbori/general_boolean_polynomial.py +++ /dev/null @@ -1,616 +0,0 @@ -from __future__ import print_function - -import sys -import resource -from .gbcore import * -from time import time, clock -from .PyPolyBoRi import * -from .blocks import declare_ring, Block -from math import sqrt -from .parallel import groebner_basis_first_finished -from optparse import OptionParser -from .interred import interred - - -# Just for debugging -def print_matrix(A): - res = "" - for i in range(len(A)): - for j in range(len(A[i])): - res += str(A[i][j]) + " " - res += "\n" - return res - - -# TODO: Implement constructor to convert Polynomials to -# GeneralBooleanPolynomial (with e_1 + ... + e_k as coefficient) -class GeneralBooleanPolynomial: - r""" - Class to represent Boolean polynomials over F_2^k - """ - - def __init__(self, k, coeff, polynomial): - r""" - Construct a GeneralBooleanPolynomial given by coeff * polynomial - - INPUT: - - - ``k`` -- Number of factors of F_2 - - ``coeff`` -- Array containing natural numbers in {0, ..., k-1} representing an element of F_2^k as a set - - ``polynomial`` -- Polynomial - """ - #print "type of polynomials", type(polynomial) - #print "polynomials is int?", isinstance(polynomial, int) - assert isinstance(polynomial, Polynomial) or isinstance(polynomial, - Monomial) or isinstance(polynomial, Variable) or isinstance( - polynomial, int) - self.polys = [] - self.k = k - self.ring = polynomial.ring() - for i in range(k): - if i in coeff: - self.polys.append(polynomial) - else: - self.polys.append(Polynomial(0, self.ring)) - - def __len__(self): - r""" - Returns the number of factors k in the product of the underlying ring F_2^k - """ - return self.k - - def __getitem__(self, k): - r""" - Return the k-th component (i.e. the projection to the k-th factor) - """ - return self.polys[k] - - def __setitem__(self, k, value): - r""" - Sets the k-th component (i.e. the projection to the k-th factor) - """ - self.polys[k] = value - - def __eq__(self, other): - r""" - Tests equality by testing that - - both objects are defined over the same ring (i.e. the number of factors is the same) - - the objects are equal in each component - """ - if not len(self) == len(other): - return False - for i in range(len(self)): - if self[i] != other[i]: - return False - return True - - def __ne__(self, other): - return not self == other - - def __str__(self): - r""" - Returns a representation of the polynomial as string - """ - res = "" - # Build a list of all terms occurring - terms = set([]) - for i in range(self.k): - if not isinstance(self.polys[i], Polynomial): - assert isinstance(self.polys[i], Monomial) or isinstance(self. - polys[i], Variable) - self[i] = Polynomial(self[i]) - terms = terms.union(set(self[i].terms())) - # Sort the terms - terms = list(terms) - terms.sort() - # Determine the coefficient for each term and build up the string - for t in terms: - comps = [j for j in range(self.k) if t in set(Polynomial(self[j]) - .terms())] - if len(comps) == self.k: - # We use this simplified notation of the coefficient it 1 - res += str(t) + " + " - else: - res += str(comps) + " * " + str(t) + " + " - res = res[0:len(res) - 3] - if res == "": - return "0" - else: - return res - - def __add__(self, other): - r""" - Addition of two GeneralBooleanPolynomial - """ - if not len(self) == len(other): - print("Cannot add polynomials defined over different rings") - print("Len(self)=", len(self)) - print("Len(other)=", len(other)) - assert len(self) == len(other) - sum = GeneralBooleanPolynomial(self.k, [], Polynomial(0, self.ring)) - for i in range(self.k): - sum[i] = self[i] + other[i] - return sum - - def __mul__(self, other): - r""" - Multiplication of two GeneralBooleanPolynomial - """ - if not len(self) == len(other): - print("Cannot multiply polynomials defined over different rings") - print("Len(self)=", len(self)) - print("Len(other)=", len(other)) - assert len(self) == len(other) - prod = GeneralBooleanPolynomial(self.k, [], Polynomial(0, self.ring)) - for i in range(self.k): - prod[i] = Polynomial(self[i]) * Polynomial(other[i]) - return prod - - def __sub__(self, other): - r""" - Subtraction of two GeneralBooleanPolynomial - """ - if not len(self) == len(other): - print("Cannot subtract polynomials defined over different rings") - print("Len(self)=", len(self)) - print("Len(other)=", len(other)) - assert len(self) == len(other) - sub = GeneralBooleanPolynomial(self.k, [], Polynomial(1, self.ring)) - for i in range(self.k): - sub[i] = self[i] - other[i] - return sub - - def lc(self): - r""" - Returns leading coefficient as constant GeneralBooleanPolynomial - """ - return GeneralBooleanPolynomial(self.k, self.lc_as_set(), - Polynomial(1, self.ring)) - - def lc_as_set_array(self): - r""" - Returns leading coefficient as array containing the indices of the - non-zero components of the leading coefficient. - """ - max_term = 1 - for i in range(len(self)): - if not self[i].is_zero(): - if self[i].lead() > max_term: - max_term = self[i].lead() - comps = [j for j in range(len(self)) if max_term - in set(self[j].terms())] - return comps - - def lc_as_set(self): - r""" - Returns leading coefficient as set containing the indices of the - non-zero components of the leading coefficient. - """ - return set(self.lc_as_set_array()) - - def lc_binary(self): - r""" - Returns leading coefficient as array containing the integers 0 or 1 - representing the leading coefficient in a binary form. - """ - lc_set = self.lc_as_set() - lc_binary = [0] * len(self) - for i in range(len(self)): - if i in lc_set: - lc_binary[i] = 1 - return lc_binary - - def lt(self): - r""" - Leading term in form of a GeneralBooleanPolynomial - """ - max_term = 1 - for i in range(self.k): - if not self[i].is_zero(): - if self[i].lead() > max_term: - max_term = self[i].lead() - comps = [j for j in range(self.k) if max_term in set(self[j].terms())] - return GeneralBooleanPolynomial(self.k, comps, max_term) - - def lm(self): - r""" - Leading monomial in form of a GeneralBooleanPolynomial - """ - max_term = 1 - contains_non_zero_term = False - for i in range(len(self)): - if not self[i].is_zero(): - contains_non_zero_term = True - if self[i].lead() > max_term: - max_term = self[i].lead() - if not contains_non_zero_term: - raise ValueError("lm of zero polynomial is not defined") - return GeneralBooleanPolynomial(self.k, [i for i in range(self.k)], - max_term) - - def constant_part_binary(self): - r""" - Constant part as binary tuple indicading which coefficients are non-zero - """ - comps = [] - for i in range(len(self)): - if self[i].has_constant_part(): - comps.append(1) - else: - comps.append(0) - return comps - - def constant_part_as_set_array(self): - r""" - Constant part as array containing the indices of the non-zero coefficients of the constant part (sorted increasingly) - """ - res = [] - for i in range(len(self)): - if self[i].has_constant_part(): - res.append(i) - return res - - def constant_part_as_set(self): - r""" - Constant part as set containing the indices of the non-zero coefficients of the constant part - """ - return set(self.constant_part_as_set_array()) - - def constant_part(self): - r""" - Constant part as GeneralBoolenPolynomial - """ - res = GeneralBooleanPolynomial(len(self), [], Polynomial(0, self.ring)) - for i in range(len(self)): - if self[i].has_constant_part(): - res[i] = Polynomial(1, self.ring) - else: - res[i] = Polynomial(0, self.ring) - return res - - def to_expanded_polynomial_ring(self, new_variables): - r""" - Returns a representation in form of a Polynomial over a ring with - additional variables, one for each factor in the product of fields - F_2^k - """ - assert self.k == len(new_variables) - return sum(new_variables[i] * self.polys[i] for i in range(self.k)) - - def is_monomial(self): - r""" - Test if self is a Monomial - """ - # Build a list of all terms occurring - terms = set([]) - for i in range(self.k): - if not isinstance(self.polys[i], Polynomial): - assert isinstance(self.polys[i], Monomial) or isinstance(self. - polys[i], Variable) - self[i] = Polynomial(self[i]) - terms = terms.union(set(self[i].terms())) - if len(terms) > 1: - return False - else: - return True - - def is_zero(self): - r""" - Tests if self is zero - """ - for i in range(len(self)): - if self[i] != 0: - return False - return True - - def monomial(self): - r""" - Returns a PolyBoRi Monomial representing the leading monomial of self, where self should be a monomial - """ - assert self.is_monomial() - for i in range(self.k): - if self.polys[i] != Polynomial(0, self.ring): - return self.polys[i].lead() - return Polynomial(0, self.ring) - - def divides(self, other): - r""" - Tests if self divides other - """ - assert len(self) == len(other) - assert (self.is_monomial() and other.is_monomial()) - self_divides_other = True - for i in range(len(self)): - if self[i] == 0: - if other[i] != 0: - return False - else: - continue - if self[i] == 1: - continue - else: - if other[i] == 1: - return False - - if other[i] == 0: - continue - - assert other[i] == other[i].lead() - other[i] = other[i].lead() - if not other[i] in (Polynomial(self.polys[i]).lead()).divisors(): - return False - return True - - -# Simple Gaus algorithm -# Should really be replaced by something faster (maybe from PolyBoRi) -def triangulate_over_F2(A, b): - assert len(A) == len(b) - n = len(b) - m = len(A[0]) - print("n: ", n, "m: ", m) - print("A, b", A, b) - for i in range(0, min(n, m)): # Row - print("i", i) - if A[i][i] == 0: - # permutate rows - changed = False - for l in range(i, n): - if A[l][i] != 0: - for k in range(n): - A[l][k], A[i][k] = A[i][k], A[l][k] - b[l], b[i] = b[i], b[l] - changed = True - if not changed: - return -1 - for j in range(0, i): - if A[i][j] != 0: - for k in range(j, n): - A[i][k] += A[i - 1][k] - b[i] += b[i - 1] - res_A = [[A[i][j] % 2 for j in range(m)] for i in range(n)] - res_b = [b[i] % 2 for i in range(n)] - return (res_A, res_b) - - -def projection_of_expanded_polynomial(f, e, e_vars): - r""" - Compute the projection of the expanded polynomial f to the component - corresponding to the variable e (which is part of e_vars) - """ - for v in e_vars: - if v == e: - # Substitute v by 1 - f = Polynomial(f.set().subset0(v.index())) + Polynomial(f.set(). - subset1(v.index())) - else: - # Substitute v by 0 - f = Polynomial(f.set().subset0(v.index())) - return f - - -def expanded_polynomial2general_polynomial(polynomial, new_variables, ring): - r""" - Returns a GeneralBooleanPolynomial associated to a Polynomial (polynomial) in - additional variables (new_variables), where the - GeneralBooleanPolynomial in obtained from the projections of polynomial - to the factors of F_2^k (by specialization of the additional variables) - """ - comps = [projection_of_expanded_polynomial(polynomial, e, new_variables) - for e in new_variables] - sum2 = GeneralBooleanPolynomial(len(new_variables), [0] * len( - new_variables), Polynomial(0, ring)) - for i in range(len(new_variables)): - sum2[i] = comps[i] - return sum2 - - -def reduce_general_boolean_polynomial(F, polynomial): - r""" - Computes the reduction of polynomial via the ideal given by F - """ - r = polynomial - s = len(F) - k = len(polynomial) - h = [GeneralBooleanPolynomial(len(polynomial), [0] * len(polynomial), - Polynomial(0, self.ring))] * s - # Indices i where leading monomial of F[i] divided leading monomial of r - - def calc_Is(): - ret = [] - for i in range(s): - if (F[i].is_zero() and not r.is_zero()): - continue - if (F[i].lm()).divides(r.lm()): - ret.append(i) - return ret - - def calc_X(): - ret = [] - for i in range(len(Is)): - ret.append((r.lm()).monomial() / (F[Is[i]].lm()).monomial()) - return ret - - def included(i, set): - if i in set: - return 1 - else: - return 0 - - Is = calc_Is() - X = calc_X() - - for f in F: - assert len(f) == len(polynomial) - - lm_polynomial = (polynomial.lm()).monomial() - lc_polynomial_binary = polynomial.lc_binary() - - while len(Is) != 0: - exp = [F[i].lm() for i in range(s)] - matrix = [[included(i, F[j].lc_as_set()) for j in Is] for i in range( - k)] - - # Compute solution - coeff = [[0] * len(Is) for j in range(k)] - for j in range(k): - if lc_polynomial_binary[j]: - coeff[j][0] = 1 - - sum = GeneralBooleanPolynomial(k, [0] * k, Polynomial(0, self.ring)) - for i in range(len(Is)): - c = [coeff[l][i] for l in range(k)] - c_set = [l for l in range(k) if coeff[l][i] == 1] - poly1 = GeneralBooleanPolynomial(k, c_set, X[i]) - - sum += GeneralBooleanPolynomial(k, c_set, X[i]) * F[Is[i]].lt() - - if polynomial.lt() == sum: - r -= sum - else: - break - - if r.is_zero(): - return r - - Is = calc_Is() - X = calc_X() - return r - - -def stratified(I): - r""" - Tests if I does no contain two polynomials with the same leading monomials - """ - leading_monomials = [] - for p in I: - if p.is_zero(): - continue - lm = p.lm() - if lm in leading_monomials: - return False - leading_monomials.append(lm) - return True - - -def build_dict_from_array_of_extended_polynomials(A, e_vars): - new_dict = {} - for p in A: - new_dict[p] = expanded_polynomial2general_polynomial(p, e_vars) - return new_dict - - -def stratify_dict_I_gb_I(dict, e_vars, debug=0): - r""" - Wrapper (calls either stratify_dict_I_gb_I_our_alg or stratify_dict_I_gb_I_Inoue - """ - #return stratify_dict_I_gb_I_Inoue(dict, e_vars, debug) - return stratify_dict_I_gb_I_our_alg(dict, e_vars, debug) - - -def stratify_dict_I_gb_I_our_alg(dict, e_vars, debug=0): - r""" - Build a stratified Groebner bases for dict - """ - # Ideal for the polynomials of the new basis - A = [] - # and their leading monomials - LMs = [] - new_dict = {} - - - while len(dict) > 0: - # We copy the keys of dict into I in order to sort them. - # This way the elements are traversed in a unique order. - I = sorted(dict.keys()) - p = I[0] - - p_gb = dict[p] - - del dict[p] - - if debug > 1: - print("Processing p", p) - print("A before proceeding", A) - - if p_gb.is_zero(): - LMs.append(Polynomial(0, self.ring)) - A.append(p) - if debug > 1: - print("Adding p that becomes zero") - continue - - p_gb_lm = p_gb.lm() - - # If the leading monomial of p does not coincide with any of - # polynomials already in A, then add p to A - if not p_gb_lm in LMs: - A.append(p) - LMs.append(p_gb_lm) - if debug > 1: - print("Appending", p, "since its lm is not contained in A yet") - continue - - # Index of p_gb_lm in LMs - i = LMs.index(p_gb_lm) - - # Polynomial in A with the same lm as p - b = A[i] - - # The Polynomial we want to add to A while keeping A stratified - r = p - - b_gb = expanded_polynomial2general_polynomial(b, e_vars) - r_gb = expanded_polynomial2general_polynomial(r, e_vars) - - # Leading coefficients as GeneralBooleanPolynomial - lc_b_gb = b_gb.lc() - lc_r_gb = r_gb.lc() - unit = GeneralBooleanPolynomial(len(e_vars), [o for o in range(len( - e_vars))], Polynomial(1, p.ring())) - - b1_gb = b_gb * (unit + lc_r_gb) + r_gb - r2_gb = r_gb * (unit + lc_r_gb + lc_r_gb * lc_b_gb) + lc_r_gb * b_gb - - b1 = b1_gb.to_expanded_polynomial_ring(e_vars) - r2 = r2_gb.to_expanded_polynomial_ring(e_vars) - - A[i] = b1 - if debug > 1: - print("New polynomial in A (replaced)", A[i]) - - if r2 != 0 and r2 not in A: - dict[r2] = r2_gb - - return build_dict_from_array_of_extended_polynomials(A, e_vars) - - -def stratify_dict_I_gb_I_Inoue(dict, e_vars, debug=0): - r""" - Reimplementation of a simple algorithm of Inoue from BGSet - """ - # Ideal for the polynomials of the new basis - A = [] - new_dict = {} - while len(dict) > 0: - p = dict.keys()[0] - p_gb = dict[p] - del dict[p] - - if p_gb.is_zero(): - A.append(p) - continue - - p_gb_lm = p_gb.lm() - - for f in dict.keys(): - f_gb = dict[f] - - if f_gb.is_zero(): - continue - - if p_gb_lm == f_gb.lm(): - p = p + f - del dict[f] - A.append(p) - return build_dict_from_array_of_extended_polynomials(A, e_vars) diff --git a/src/sage/rings/polynomial/pbori/intersect.py b/src/sage/rings/polynomial/pbori/intersect.py deleted file mode 100644 index 774ca868d11..00000000000 --- a/src/sage/rings/polynomial/pbori/intersect.py +++ /dev/null @@ -1,48 +0,0 @@ -r""" -intersect.py -PolyBoRi - -Created by Michael Brickenstein on 2008-09-24. -Copyright 2008 The PolyBoRi Team -""" - -from .gbcore import groebner_basis -from .statistics import used_vars_set -from itertools import chain - - -def intersect(i, j, **gb_opts): - r""" - This functions intersects two ideals. The first ring variable is used as helper variable for this - intersection. It is assumed, that it doesn't occur in the ideals, and that we have an elimination ordering - for this variables. Both assumptions are checked. - - TESTS:: - - sage: from sage.rings.polynomial.pbori.frontend import declare_ring - sage: from sage.rings.polynomial.pbori import Block - sage: r=declare_ring(Block("x", 1000), globals()) - sage: x = r.variable - sage: intersect([x(1),x(2)+1],[x(1),x(2)]) - [x(1)] - """ - if not i or not j: - return [] - - uv = used_vars_set(i) * used_vars_set(j) - t = next(iter(i)).ring().variable(0) - if uv.reducible_by(t): - raise ValueError("First ring variable has to be reserved as helper variable t") - if not t > uv: - raise ValueError("need elimination ordering for first ring variable") - gb = groebner_basis(list(chain((t * p for p in i), ((1 + t) * p for p in j - ))), **gb_opts) - return [p for p in gb if p.navigation().value() > t.index()] - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() diff --git a/src/sage/rings/polynomial/pbori/intpolys.py b/src/sage/rings/polynomial/pbori/intpolys.py deleted file mode 100644 index 9d000a566f4..00000000000 --- a/src/sage/rings/polynomial/pbori/intpolys.py +++ /dev/null @@ -1,90 +0,0 @@ -if __name__ == '__main__': - from sys import path as search_path - from os import path as file_path - search_path.append(file_path.join(file_path.dirname(__file__), '..')) - -from .PyPolyBoRi import Monomial, Polynomial, BooleSet, BooleConstant -from .PyPolyBoRi import Variable as VariableType - - -class IntegerPolynomial(object): - """Polynomial with positive integer coefficients""" - - def __init__(self, boolean_polys): - super(IntegerPolynomial, self).__init__() - if not isinstance(boolean_polys, dict): - boolean_polys = dict([(0, Polynomial(boolean_polys))]) - self.boolean_polys = boolean_polys - - def __coerce__(self, other): - #TODO handle long - if isinstance(other, int) and other >= 0: - i = 0 - res = [] - while 2 ** i <= other: - and_ = 2 ** i & other - if and_: - res.append(i) - return (self, IntegerPolynomial(dict([(i, BooleConstant(1)) for i - in res]))) - if not isinstance(other, IntegerPolynomial): - other = Polynomial(other) - return (self, IntegerPolynomial(dict([(0, other)]))) - - def __add__(self, other): - r""" - - TESTS:: - - sage: from sage.rings.polynomial.pbori import * - sage: r= declare_ring([Block("x",1000)], globals()) # doctest: +ELLIPSIS - sage: p=IntegerPolynomial(x(1)) - sage: p - {0: x(1)} - sage: p=p+p;p - {1: x(1)} - sage: p=p+x(2); p - {0: x(2), 1: x(1)} - sage: p+p - {1: x(2), 2: x(1)} - """ - if not isinstance(other, IntegerPolynomial): - (self, other) = coerce(self, other) - return self + other - assert isinstance(other, IntegerPolynomial) - - def add_simple_poly(p, i): - p = Polynomial(p) - if p.is_zero(): - return - res_p = Polynomial(res.get(i, Polynomial(0, p.ring()))) - res[i] = res_p + p - if res[i].is_zero(): - del res[i] - inter = BooleSet(res_p).intersect(BooleSet(p)) - add_simple_poly(inter, i + 1) - return - res = dict(self.boolean_polys.items()) - for (k, p) in other.boolean_polys.items(): - add_simple_poly(p=p, i=k) - return IntegerPolynomial(res) - - def __unicode__(self): - return unicode(self.boolean_polys) - - def __str__(self): - return str(self.boolean_polys) - - def __repr__(self): - try: - return unicode(self) - except NameError: - return str(self) - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() diff --git a/src/sage/rings/polynomial/pbori/ncf.py b/src/sage/rings/polynomial/pbori/ncf.py deleted file mode 100644 index 0605a99605f..00000000000 --- a/src/sage/rings/polynomial/pbori/ncf.py +++ /dev/null @@ -1,41 +0,0 @@ -from . import Polynomial - - -def get_splitting(f, variables): - ring = s.ring() - s = f.set() - for var_index in variables: - s1 = Polynomial(s.subset1(var_index)) - s0 = Polynomial(s.subset0(var_index)) - f1 = s0 + s1 - f0 = s0 - var = ring.variable(var_index) - if f1.constant(): - return dict(b=f1, a=Polynomial(1, ring), x=var, g=f0) - if f0.constant(): - return dict(b=f0, a=Polynomial(0, ring), x=var, g=f1) - return None - - -def nested_canalyzing_function(f, variables=None): - f = Polynomial(f) - if variables is None: - variables = f.vars_as_monomial() - if not variables.reducible_by(f.vars_as_monomial()): - raise ValueError - if variables != f.vars_as_monomial(): - return False - if len(variables) == 0: - return True - splitting = get_splitting(f, variables) - if splitting is None: - return False - rec = nested_canalyzing_function(splitting["g"], variables / splitting["x" - ]) - if rec: - if rec is True: - return (splitting, ) - else: - return (splitting, rec) - else: - return False diff --git a/src/sage/rings/polynomial/pbori/parsegat.py b/src/sage/rings/polynomial/pbori/parsegat.py deleted file mode 100644 index 3c92d67c5d3..00000000000 --- a/src/sage/rings/polynomial/pbori/parsegat.py +++ /dev/null @@ -1,534 +0,0 @@ -from __future__ import print_function -#import pathadjuster - -if __name__ == '__main__': - from sys import path as search_path - from os import path as file_path - search_path.append(file_path.join(file_path.dirname(__file__), '..')) - - -def _exists(): - r""" - PolyBoRi convention: checking optional components for prerequisites here - - TESTS:: - - sage: from sage.rings.polynomial.pbori.parsegat import * - sage: _exists() - True - """ - try: - import pyparsing - except ImportError: - return False - - return True - -#from .gbrefs import declare_ring -from . import * -from .ll import ll_encode -from optparse import OptionParser -from .statistics import used_vars_set -from itertools import chain -try: - from cStringIO import StringIO -except ImportError: - from io import StringIO -from sys import stderr -parser = OptionParser() - -gat_max = 20000 -next_max = 2000 -output_max = 2000 -inter_max = 20000 -input_max = 2000 -state_max = 2000 - -# declare_ring([Block("x",gat_max),Block("xnext",next_max,reverse = -# True),Block("xoutput",output_max,reverse = -# True),Block("xinter",inter_max,reverse = -# True),Block("xinput",input_max,reverse = -# True),Block("xstate",state_max,reverse = True)],globals()) input order is -# antitopological we reverse, when mapping to final variables -parser.add_option("--forward-iteration", - action="store_true", dest="forward", default=True, - help="switch between forward and backward iteration") - -parser.add_option("--backward-iteration", - action="store_false", dest="forward", default=True, - help="switch between forward and backward iteration") - -parser.add_option("--include-outputs", - action="store_true", dest="include_outputs", default=True, - help="include circuit outputs") - -parser.add_option("--initialize-state", action="store", dest="initialize", - default="noinit", type="choice", choices=["noinit", "random", "zero"], - help="initialize state variables/only useful with forward iteration") - - -def parse_identifier(str, log, tokens): - return int(tokens[0]) - - -def throw_away(str, log, tokens): - return "None" - -assigned = set() -from random import Random - - -def add_negated(str, log, tokens): - p = tokens[1] - l = p.lead() - if not l in assigned and r.randint(0, 200) == 0: - assigned.add(l) - print("NEG", p + 1) - return p + 1 - else: - return "NONE" - - -class FamiliarityException(Exception): - r""" - Docstring for FamiliarityException - """ - - def __init__(self): - super(FamiliarityException, self).__init__() - - -def fix_symbol_name(str, log, tokens): - return (tokens[0].replace("-", "_").replace("]", "").replace("[", ""). - replace("/", "_")) - - -class DeterminingEquation(object): - r""" - Docstring for DeterminingEquation - """ - - def __init__(self, variable, mapped_to): - super(DeterminingEquation, self).__init__() - self.variable = variable - self.mapped_to = mapped_to - - def _get_equation(self): - return self.variable + self.mapped_to - equation = property(_get_equation) - - -# be careful: for next state/output we directly generate mapped_to + value -# instead of introducing a variable and mapping it later -class VariableManager(object): - r""" - Docstring for VariableManager - """ - - def __init__(self, ring, prefix="", initialize="noinit", **kwd): - super(VariableManager, self).__init__() - self.ring = ring - self.output = [] - self.input = [] - self.state = [] - self.next = [] - self.deletion_candidates = [] - self.intermediate = [] - - self.next_state_equations = [] - self.intermediate_equations = [] - self.__map = dict() - self.prefix = prefix - self.initialize = initialize - self.ands = dict() - self.xors = dict() - self.tails = [] - for (k, v) in kwd.items(): - setattr(self, k, v) - - def gauss(self, determining_equations): - l = [] - res = [] - output_set = set(self.output) - for p in determining_equations: - eq = p.equation - if not p.variable in output_set and eq.deg() == 1: - assert eq.lead() == p.variable - l.append(eq) - self.deletion_candidates.append(p.variable) - else: - res.append(p) - l = gauss_on_polys(l) - for p in l: - res.append(DeterminingEquation(p.lead(), p + p.lead())) - return res - - def ideals(self): - def sort_key(p): - return p.navigation().value() - - def my_sort(l): - return sorted(l, key=sort_key) - #self.intermediate_equations = self.gauss(self.intermediate_equations) - tail_variables = set(used_vars_set([e.mapped_to for e in chain(self. - next_state_equations, self.intermediate_equations)]).variables()) - to_delete = [] - for v in self.deletion_candidates: - if not v in tail_variables: - to_delete.append(v) - to_delete = set(to_delete) - self.intermediate_equations = [e for e in self.intermediate_equations - if not e.variable in to_delete] - - # we don't delete the variable itself, as we will confuse - # gluemultipliers,that looks on the lengths of self.intermediate... - def equations(determining_equations): - return [e.equation for e in determining_equations] - ideal_state = [] - ideal_next_state = equations(self.next_state_equations) - ideal_intermediate = equations(self.intermediate_equations) - - if self.initialize != "noinit": - if self.initialize == "zero": - initializer = zero_fun - else: - initializer = random_bit - for v in variables: - if str(v)[:1] == "s": - ideal_state.append(v + initializer()) - - return [ - my_sort(self.apply_map(i)) for i in - (ideal_state, ideal_intermediate, ideal_next_state)] - - def set_variable_name(self, i, name): - _set_variable_name(self.ring, i, self.prefix + name) - - def parse_output_action(self, str, log, tokens): - p = Polynomial(tokens[1]) - #self.output.append(p) - #mapped_to = xoutput(len(self.output)-1) - mapped_to = self.xoutput(len(self.output)) - self.output.append(mapped_to) - self.set_variable_name(mapped_to.index(), "out_" + tokens[2]) - self.tails.append(p) - if self.include_outputs: - self.intermediate_equations.append(DeterminingEquation(mapped_to, - p)) - return mapped_to + p - else: - return "NONE" - - def parse_buffer_action(self, str, log, tokens): - return "NONE" - - def parse_next_state_action(self, str, log, tokens): - p = Polynomial(tokens[1]) - #self.next.append(p) - #mapped_to = xnext(len(self.next)-1) - mapped_to = self.xnext(len(self.next)) - self.next.append(mapped_to) - self.set_variable_name(mapped_to.index(), "nstate_" + tokens[2]) - self.next_state_equations.append(DeterminingEquation(mapped_to, p)) - self.tails.append(p) - return mapped_to + p - - def parse_state_action(self, str, log, tokens): - p = self.x(tokens[0]) - #self.state.append(p) - #mapped_to = xstate(len(self.state)-1) - mapped_to = self.xstate(len(self.state)) - self.state.append(mapped_to) - self.set_variable_name(mapped_to.index(), "state_" + tokens[2]) - self.map(p, mapped_to) - return "NONE" - - def parse_input_action(self, str, log, tokens): - p = self.x(tokens[0]) - #self.input.append(p) - #mapped_to = xinput(len(self.input)-1) - mapped_to = self.xinput(len(self.input)) - self.input.append(mapped_to) - self.set_variable_name(mapped_to.index(), "in_" + tokens[2]) - self.map(p, mapped_to) - return "NONE" - - def evaluates_to_variables(self, signal): - ands = self.ands[signal] - return list( - used_vars_set(ands).variables()) - - def eval_and_gat(self, lit, inputs=None): - if inputs is None: - inputs = self.ands[lit.lex_lead()] - assert len(inputs) > 0 - res = inputs[0] - for i in inputs[1:]: - res = res * i - if lit.has_constant_part(): - res = res + 1 - return res - - def simplified_product(self, out, op1, op2): - - if op1.deg() == 1 and op2.deg() == 1: - op1v = op1.lex_lead() - op2v = op2.lex_lead() - - if op1v not in self.xors \ - and op2v not in self.xors \ - and op1v in self.ands \ - and op2v in self.ands: - mapped_to = [p for p in chain(self.ands[op1v], self.ands[op2v])] - for p in mapped_to: - if p.deg() != 1: - return op1 * op2 - mapped_to = set([p.lex_lead() for p in mapped_to]) - #print >>stderr, op1, op2, mapped_to - if len(mapped_to) <= 2: - #xor pattern - self.deletion_candidates.extend([op1v, op2v]) - assert op1.vars_as_monomial().deg() == 1 - assert op2.vars_as_monomial().deg() == 1 - op1 = self.eval_and_gat(op1) - op2 = self.eval_and_gat(op2) - outer_res = self.eval_and_gat(out, [op1, op2]) - self.xors[out] = outer_res - else: - try: - for (op1, op1v, op2, op2v) in \ - [(op1, op1v, op2, op2v), (op2, op2v, op1, op1v)]: - (op1e, op2e) = [ - self.evaluates_to_variables(left_parent) - for left_parent in (op1v, op2v)] - for (i, s) in enumerate(op2e): - if s in self.xors or s not in self.ands: - continue - - if self.evaluates_to_variables(s) == op1e: - raise FamiliarityException() - except FamiliarityException: - op1 = self.eval_and_gat(op1) - op2_inputs = list(self.ands[op2v]) - for (i, s2) in enumerate(op2_inputs): - if s2.lex_lead() == s: - op2_inputs[i] = self.eval_and_gat(s2) - op2 = self.eval_and_gat(op2, inputs=op2_inputs) - self.deletion_candidates.extend((s, op1v, op2v)) - else: - # pattern - # 34 AND ~16 ~15 - # 35 AND 14 34 - # 36 AND 16 15 - # 37 AND ~14 36 - # 38 AND ~37 ~35 - - try: - (op1a, op2a) = [self.ands.get(v) - for v in (op1v, op2v)] - assert len(op1a) == 2 - assert len(op2a) == 2 - print("+", file=stderr) - for op1a in [[op1a[0], op1a[1]], op1a]: - for op2a in [[op2a[0], op2a[1]], op2a]: - print(op1a[0], op2a[0], file=stderr) - if op1a[0] == op2a[0] + 1: - print("-", file=stderr) - if op1a[1] in self.ands and \ - op2a[1] in self.ands and \ - op1a[1] not in self.xors \ - and op2a[1] not in self.xors: - if set(self.ands[op1a[1]]) == set([1 - + s for s in self.ands[op2a[1]]]): - raise FamiliarityException - except FamiliarityException: - self.deletion_candidates.extend([op1v, op2v, op2a[1]. - lex_lead(), op1a[1].lex_lead()]) - for group in (op2a, op1a): - group[1] = self.eval_and_gat(group[1]) - (op1, op2) = [ - self.eval_and_gat(op, inputs) for (op, inputs) - in [(op1, op1a), (op2, op2a)]] - - return op1 * op2 - - def parse_and_action(self, string, log, tokens): - - inter = self.x(tokens[0]) - op1 = tokens[2] - op2 = tokens[3] - #self.intermediate.append(inter) - #mapped_to = xinter(len(self.intermediate)-1) - mapped_to = self.xinter(len(self.intermediate)) - self.ands[inter] = (op1, op2) - self.intermediate.append(mapped_to) - self.set_variable_name(mapped_to.index(), "y" + str(tokens[0])) - self.map(inter, mapped_to) - tail = self.simplified_product(inter, op1, op2) - self.tails.append(tail) - eq = inter + tail - self.intermediate_equations.append(DeterminingEquation(inter, tail)) - return eq - - def map(self, from_, to): - self.__map[from_] = to - - def apply_map(self, eq): - encoded = ll_encode((k + v for (k, v) in self.__map.items())) - return [ll_red_nf_noredsb(p, encoded) for p in eq] - - def parse_sign(self, str, log, tokens): - if list(tokens) != [0]: - return [1] - else: - return tokens - - def parse_idenfifier_ref(self, str, log, tokens): - if tokens[1] in (0, 1): - #from sys import stderr - #stderr.write("TOKENS:"+repr(tokens)) - #stderr.flush() - return Polynomial(tokens[0] + tokens[1]) - return tokens[0] + self.x(tokens[1]) - - -def generate_gat_bnf(manager): - - from pyparsing import (Literal, CaselessLiteral, Word, Combine, Group, - Optional, ZeroOrMore, Forward, nums, alphas, Or, restOfLine, - OneOrMore, restOfLine, alphanums) - - identifier = Word(nums).setParseAction(parse_identifier) - symbolic_name = (Word(alphanums + "_-[]/", alphanums + "_-[]/")). \ - setParseAction(fix_symbol_name) - meta = ZeroOrMore( - Or( - [ - CaselessLiteral("WRITER"), - CaselessLiteral("DESIGN"), - CaselessLiteral("ORIGIN")]) + restOfLine) - end = CaselessLiteral("END") - and_ = CaselessLiteral("AND") - input_ = CaselessLiteral("INPUT") - state = CaselessLiteral("STATE") - nstate = CaselessLiteral("NSTATE") - output = CaselessLiteral("OUTPUT") - buffer_ = CaselessLiteral("BUFFER") - identifier_ref = \ - (Optional(Literal("~"), default=0).setParseAction( - manager.parse_sign) + identifier).setParseAction( - manager.parse_idenfifier_ref) - - input_line = (identifier + input_ + symbolic_name).setParseAction(manager. - parse_input_action) - and_line = (identifier + and_ + identifier_ref + identifier_ref). \ - setParseAction(manager.parse_and_action) - buffer_line = (buffer_ + identifier_ref + symbolic_name).setParseAction( - manager.parse_buffer_action) - output_line = (output + identifier_ref + symbolic_name).setParseAction( - manager.parse_output_action) - state_line = (identifier + state + symbolic_name).setParseAction(manager. \ - parse_state_action) - nstate_line = (nstate + identifier_ref + symbolic_name).setParseAction( - manager.parse_next_state_action) - assignment = Or([output_line, and_line, input_line, state_line, - nstate_line, buffer_line]) - - gat = meta + OneOrMore(assignment) + end - return gat -generator = Random(123) - - -def parse(f, manager): - f = open(f) - content = f.read() - bnf = generate_gat_bnf(manager) - parsed = bnf.parseString(content) - f.close() - - -def zero_fun(): - return 0 - - -def random_bit(): - return generator.randint(0, 1) - - -def format_grouped(l, group_size=10, indent=0): - s = StringIO() - s.write("[") - last = len(l) - 1 - for (i, e) in enumerate(l): - if i % group_size == 0: - s.write("\n" + indent * " ") - s.write(e) - if i != last: - s.write(", ") - s.write("]") - return s.getvalue() - - -def generate_three_ideal_output(ideal_state, ideal_intermediate, - ideal_next_state, variables): - print("declare_ring(" + format_grouped([repr(str(v)) for v in variables], - indent=4) + ")") - print("block_start_hints=" + repr(block_starts)) - print("ideal_intermediate=[") - print(",\n".join((str(p) for p in ideal_intermediate))) - print("]") - print("ideal_state=[") - print(",\n".join((str(p) for p in ideal_state))) - print("]") - print("ideal_next_state=[") - print(",\n".join((str(p) for p in ideal_next_state))) - print("]") - print("ideal = ideal_state+ideal_next_state+ideal_intermediate") - -if __name__ == '__main__': - (options, args) = parser.parse_args() - kwd_args = dict() - ring = declare_ring([ - "t", - Block("x", gat_max, reverse=True), - Block("xnext", next_max, reverse=True), - Block("xoutput", output_max, reverse=True), - Block("xinter", inter_max, reverse=True), - Block("xinput", input_max, reverse=True), - Block("xstate", state_max, reverse=True)], kwd_args) - - manager = VariableManager(ring=ring, include_outputs=options. - include_outputs, initialize=options.initialize, **kwd_args) - - from sys import argv - f = args[0] - - parse(f, manager) - (ideal_state, ideal_intermediate, ideal_next_state) = manager.ideals() - ideal = ideal_state + ideal_intermediate + ideal_next_state - - variables = [] - used_vars = set(used_vars_set(ideal).variables()) - if not options.forward: - variables = list(chain(reversed(manager.next), reversed(manager.output - ), reversed(manager.intermediate), reversed(manager.input), - reversed(manager.state))) - else: - variables = list( - chain(reversed(manager.output), - reversed(manager.intermediate), - reversed(manager.input), - reversed(manager.state), - reversed(manager.next))) - variables = ["t"] + variables - beginnings = [str(v)[:1] for v in variables] - block_starts = [] - last = beginnings[0] - - for (i, s) in enumerate(beginnings): - if s != last: - block_starts.append(i) - last = s - - generate_three_ideal_output(ideal_state, ideal_intermediate, - ideal_next_state, variables) diff --git a/src/sage/rings/polynomial/pbori/partial.py b/src/sage/rings/polynomial/pbori/partial.py deleted file mode 100644 index 4bdda1fb0b7..00000000000 --- a/src/sage/rings/polynomial/pbori/partial.py +++ /dev/null @@ -1,51 +0,0 @@ -from . import BooleSet, interpolate_smallest_lex - - -class PartialFunction(object): - r""" - Docstring for PartialFunction - """ - - def __init__(self, zeros, ones): - super(PartialFunction, self).__init__() - self.zeros = zeros.set() - self.ones = ones.set() - - def interpolate_smallest_lex(self): - return interpolate_smallest_lex(self.zeros, self.ones) - - def __str__(self): - return "PartialFunction(zeros=" + str(self.zeros) + ", ones=" + str( - self.ones) + ")" - - def definedOn(self): - return self.zeros.union(self.ones) - - def __add__(self, other): - domain = self.definedOn().intersect(other.definedOn()) - zeros = self.zeros.intersect(other.zeros).union(self.ones.intersect( - other.ones)) - ones = self.zeros.intersect(other.ones).union(self.ones.intersect( - other.zeros)) - assert zeros.diff(domain).empty() - assert ones.diff(domain).empty() - return PartialFunction(zeros, ones) - - def __repr__(self): - return str(self) - - def __mul__(self, other): - zeros = self.zeros.union(other.zeros) - ones = self.ones.intersect(other.ones) - return PartialFunction(zeros, ones) - - def __or__(self, other): - zeros = self.zeros.intersect(other.zeros) - ones = self.ones.union(other.ones) - return PartialFunction(zeros, ones) - - def __xor__(self, other): - return self + other - - def __and__(self, other): - return self * other diff --git a/src/sage/rings/polynomial/pbori/plot.py b/src/sage/rings/polynomial/pbori/plot.py deleted file mode 100644 index 9387486b9c1..00000000000 --- a/src/sage/rings/polynomial/pbori/plot.py +++ /dev/null @@ -1,259 +0,0 @@ -# encoding: utf-8 -r""" -plot.py - -Created by Michael Brickenstein on 2008-10-17. -Copyright (c) 2008 The PolyBoRi Team. - -""" - - -def _exists(): - r""" - PolyBoRi convention: checking optional components for prerequisites here - - TESTS:: - - sage: from sage.rings.polynomial.pbori.plot import * - sage: _exists() - True - """ - try: - import jinja2 - except ImportError: - try: - import jinja - except ImportError: - return False - - try: - from subprocess import Popen, PIPE - from os import devnull - out = open(devnull) - process = Popen(["dot", "-V"], stderr=out, stdout=out) - out.close() - except: - return False - - return True - -import sys -import os -from .PyPolyBoRi import Ring, Polynomial, BooleSet -from subprocess import Popen, PIPE - -graph_template = """ -digraph polynomial{ -graph [ -ordering="out" -#if highlight_monomial -, label = "${display_monomial(highlight_monomial)}" -#end -, fontsize=${fontsize} -]; - -#for n in nodes -${identifier(n)}[label="${label(n)}", shape="${shape(n)}"]; -#end - -#for n in non_constant_nodes -${identifier(n)} -> ${identifier(n.else_branch())} [style="dashed", color="${color_else}", arrowhead="vee", penwidth="${penwidth_else(n)}"]; -${identifier(n)} -> ${identifier(n.then_branch())} [color="${color_then}", arrowhead="vee", penwidth="${penwidth_then(n)}"]; -#end -} -""" - -graph_template_jinja = """ -digraph polynomial{ -{% if landscape %} -rankdir=LR; -{% endif %} -graph [ ordering="out" -{% if highlight_monomial %} -, label = "{{display_monomial(highlight_monomial)}}" -{% endif %} -, fontsize={{fontsize}} -]; - -{% for n in nodes %} -{{identifier(n)}}[label="{{label(n)}}", shape="{{shape(n)}}"]; -{% endfor %} - -{% for n in non_constant_nodes %} -{{identifier(n)}} -> {{identifier(n.else_branch())}} [style="dashed", color="{{color_else}}", arrowhead="vee", penwidth="{{penwidth_else(n)}}"]; -{{identifier(n)}} -> {{identifier(n.then_branch())}} [color="{{color_then}}", arrowhead="vee", penwidth="{{penwidth_then(n)}}"]; -{% endfor %} -} -""" - -ELSE = "else" -THEN = "then" - - -def render_genshi(data_dict): - from genshi.template import TextTemplate - tmpl = TextTemplate(graph_template) - stream = tmpl.generate(**data_dict) - return str(stream) - - -def render_jinja(data_dict): - try: - from jinja2 import Environment - except: - from jinja import Environment - env = Environment() - tmpl = env.from_string(graph_template_jinja) - return tmpl.render(**data_dict) - - -def monomial_path_in_zdd(mon, graph): - res = [] - if not mon in BooleSet(graph): - raise ValueError - graph_nav = BooleSet(graph).navigation() - mon_nav = BooleSet(mon).navigation() - while not mon_nav.constant(): - while graph_nav.value() < mon_nav.value(): - res.append((graph_nav, ELSE)) - graph_nav = graph_nav.else_branch() - assert mon_nav.value() == graph_nav.value() - res.append((graph_nav, THEN)) - mon_nav = mon_nav.then_branch() - graph_nav = graph_nav.then_branch() - while not graph_nav.constant(): - res.append((graph_nav, ELSE)) - graph_nav = graph_nav.else_branch() - return dict(res) - - -def plot(p, filename, colored=True, format="png", - highlight_monomial=None, fontsize=14, - template_engine='jinja', landscape=False - ): - r""" - plots ZDD structure to in format - - EXAMPLES:: - - sage: r=Ring(1000) - sage: x = r.variable - sage: plot(x(1)+x(0),"/dev/null", colored=True) - sage: plot(x(1)+x(0),"/dev/null", colored=False) - """ - THICK_PEN = 5 - highlight_path = dict() - if highlight_monomial: - highlight_path = monomial_path_in_zdd(highlight_monomial, p) - - def display_monomial(m): - try: - return unicode(m).replace(u"*", u".") - except NameError: - return str(m).replace("*", "⋅") - - def penwidth_else(n): - if n in highlight_path and highlight_path[n] == ELSE: - return THICK_PEN - return 1 - - def penwidth_then(n): - if n in highlight_path and highlight_path[n] == THEN: - return THICK_PEN - return 1 - if not colored: - color_then = "black" - color_else = "black" - else: - color_then = "red" - color_else = "blue" - - def find_navs(nav): - if not nav in nodes: - nodes.add(nav) - if not nav.constant(): - find_navs(nav.then_branch()) - find_navs(nav.else_branch()) - p = Polynomial(p) - nodes = set() - nav = p.navigation() - find_navs(nav) - non_constant_nodes = [n for n in nodes if not n.constant()] - node_to_int = dict([(n, i) for (i, n) in enumerate(nodes)]) - - r = p.ring() - - def identifier(n): - return "n" + str(node_to_int[n]) - - def label(n): - if n.constant(): - if n.terminal_one(): - return "1" - else: - return "0" - else: - return str(r.variable(n.value())) - - def shape(n): - if n.constant(): - return "box" - else: - return "ellipse" - renderers = dict(genshi=render_genshi, jinja=render_jinja) - - dot_input = renderers[template_engine](locals()) - if not isinstance(dot_input, bytes): - dot_input = dot_input.encode('utf-8') - process = Popen(["dot", "-T" + format, "-o", filename], stdin=PIPE, - stdout=PIPE) - - process.stdin.write(dot_input) - process.stdin.close() - process.wait() - - -def main(): - r = Ring(1000) - x = Variable = VariableFactory(r) - from os import system - from .specialsets import all_monomials_of_degree_d, power_set - full_set = list(power_set([Variable(i) for i in range(15)])) - from random import Random - generator = Random(123) - random_set = sum(generator.sample(full_set, 30)) - full_polynomial = list(all_monomials_of_degree_d(3, [Variable(i) for i in - range(30)])) - random_poly = sum(generator.sample(full_polynomial, 30)) - polynomials = [ - x(1) * x(2) + x(3), - (x(1) + 1) * (x(2) + x(3)), - (x(1) + 1) * (x(2) + 1) * (x(3) + 1), - x(1) * x(2) + x(2) * x(3) + x(1) * x(3) + x(1), - x(0) + x(1) + x(2) + x(3) + x(4) + x(5), - all_monomials_of_degree_d(3, [x(i) for i in range(10)]), - power_set([x(i) for i in range(10)]), - random_poly, - random_set, - Polynomial(all_monomials_of_degree_d(3, [x(i) for i in range(10)])) + - Polynomial(power_set([x(i) for i in range(10)])), - Polynomial(power_set([x(i) for i in range(10)])) + 1 - ] - for colored in [True, False]: - if colored: - colored_suffix = "_colored" - else: - colored_suffix = "" - for format in ["png", "svg"]: - for (i, p) in enumerate(polynomials): - - #dot_file=str(i) +colored_suffix+".dot" - #f=open(dot_file, "w") - #f.write(dot) - #f.close() - out_file = str(i) + colored_suffix + "." + format - plot(p, out_file, colored=colored, format=format) - #system("dot -Tpng -o "+png_file+" " + dot_file) - -if __name__ == '__main__': - main() diff --git a/src/sage/rings/polynomial/pbori/simplebb.py b/src/sage/rings/polynomial/pbori/simplebb.py deleted file mode 100644 index a80cf185444..00000000000 --- a/src/sage/rings/polynomial/pbori/simplebb.py +++ /dev/null @@ -1,80 +0,0 @@ -from .PyPolyBoRi import * -from .interred import interred - - -def buchberger(l): - r""" - Calculates a (non minimal) Groebner basis - """ - l = interred(l) - #for making sure, that every polynomial has a different leading term - #needed for add_generator - if not l: - return [] - g = GroebnerStrategy(l[0].ring()) - for p in l: - g.add_generator(p) - while g.npairs() > 0: - g.clean_top_by_chain_criterion() - p = g.next_spoly() - p = g.nf(p) - if not p.is_zero(): - g.add_generator(p) - return list(g) - - -def less_than_n_solutions(ideal, n): - l = interred(ideal) - if not l: - return False - g = GroebnerStrategy(l[0].ring()) - all_monomials = Monomial([Variable(i) for i - in range(number_of_variables())]).divisors() - monomials_not_in_leading_ideal = all_monomials - for p in l: - g.add_generator(p) - while g.npairs() > 0: - monomials_not_in_leading_ideal = monomials_not_in_leading_ideal \ - % g.reduction_strategy.minimal_leading_terms - if len(monomials_not_in_leading_ideal) < n: - return True - g.clean_top_by_chain_criterion() - p = g.next_spoly() - p = g.nf(p) - if not p.is_zero(): - g.add_generator(p) - monomials_not_in_leading_ideal = monomials_not_in_leading_ideal \ - % g.reduction_strategy.minimal_leading_terms - if len(monomials_not_in_leading_ideal) < n: - return True - else: - return False - - -def gauss(matrix): - r""" - Toy Gaussian elimination. - - EXAMPLES:: - - sage: from sage.rings.polynomial.pbori.simplebb import * - sage: gauss([[0,1],[1,1]]) - """ - from .gbcore import groebner_basis - - def get_num(idx, vars): - if idx in [var.index() for var in vars.variables()]: - return 1 - return 0 - - nrows = len(matrix) - ncols = len(matrix[0]) - eqs = [sum([matrix[row][col] * Variable(col) for col in range(ncols)]) - for row in range(nrows)] - result = groebner_basis(eqs) - result = result + [BooleConstant(0)] * (nrows - len(result)) - - return [[get_num(idx, elt.set().vars()) for idx in range(ncols)] - for elt in result] - - return result From a0ccf77b2d4af9e5a6fde8c01c4f0d1fdfff2bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 17:05:33 +1200 Subject: [PATCH 257/379] removing unused files: were used by the previously removed files. --- src/sage/rings/polynomial/pbori/interred.py | 29 --------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/sage/rings/polynomial/pbori/interred.py diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py deleted file mode 100644 index c6634bafc23..00000000000 --- a/src/sage/rings/polynomial/pbori/interred.py +++ /dev/null @@ -1,29 +0,0 @@ -from .PyPolyBoRi import GroebnerStrategy, Polynomial, ReductionStrategy - - -def interred(l, completely=False): - r"""computes a new generating system (g1, ...,gn), - spanning the same ideal modulo field equations. - The system is interreduced: For i!=j: - gi.lead() does not divide any leading term of gj. - If completely is set to True, then also terms in the - tail are not reducible by other polynomials. - """ - l = [Polynomial(p) for p in l if not p == 0] - if not l: - return [] - ring = l[0].ring() - l_old = None - l = tuple(l) - while l_old != l: - l_old = l - l = sorted(l, key=Polynomial.lead) - g = ReductionStrategy(ring) - if completely: - g.opt_red_tail = True - for p in l: - p = g.nf(p) - if not p.is_zero(): - g.add_generator(p) - l = tuple([e.p for e in g]) - return list(l) From b3c16a8e2350f6a85142824c20ad9e61abba698a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 19:51:30 +1200 Subject: [PATCH 258/379] Revert "removing unused files: were used by the previously removed files." Turns out interred is used somewhere else. This reverts commit a0ccf77b2d4af9e5a6fde8c01c4f0d1fdfff2bee. --- src/sage/rings/polynomial/pbori/interred.py | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/sage/rings/polynomial/pbori/interred.py diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py new file mode 100644 index 00000000000..c6634bafc23 --- /dev/null +++ b/src/sage/rings/polynomial/pbori/interred.py @@ -0,0 +1,29 @@ +from .PyPolyBoRi import GroebnerStrategy, Polynomial, ReductionStrategy + + +def interred(l, completely=False): + r"""computes a new generating system (g1, ...,gn), + spanning the same ideal modulo field equations. + The system is interreduced: For i!=j: + gi.lead() does not divide any leading term of gj. + If completely is set to True, then also terms in the + tail are not reducible by other polynomials. + """ + l = [Polynomial(p) for p in l if not p == 0] + if not l: + return [] + ring = l[0].ring() + l_old = None + l = tuple(l) + while l_old != l: + l_old = l + l = sorted(l, key=Polynomial.lead) + g = ReductionStrategy(ring) + if completely: + g.opt_red_tail = True + for p in l: + p = g.nf(p) + if not p.is_zero(): + g.add_generator(p) + l = tuple([e.p for e in g]) + return list(l) From 20d1f9e9fb6e4681fe45c41daf25b915788f8295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 21:32:26 +1200 Subject: [PATCH 259/379] Fix doctests in pbori.pyx --- src/sage/rings/polynomial/pbori/pbori.pyx | 215 +++++++++++----------- 1 file changed, 108 insertions(+), 107 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 8da8c3b7de6..ddc3be9cbe7 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -1783,17 +1783,17 @@ def get_var_mapping(ring, other): sage: P. = BooleanPolynomialRing(3) sage: R. = QQ[] - sage: sage.rings.polynomial.pbori.get_var_mapping(P,R) + sage: sage.rings.polynomial.pbori.pbori.get_var_mapping(P,R) [z, y] - sage: sage.rings.polynomial.pbori.get_var_mapping(P, z^2) + sage: sage.rings.polynomial.pbori.pbori.get_var_mapping(P, z^2) [z, None] :: sage: R. = BooleanPolynomialRing(2) - sage: sage.rings.polynomial.pbori.get_var_mapping(P,R) + sage: sage.rings.polynomial.pbori.pbori.get_var_mapping(P,R) [z, x] - sage: sage.rings.polynomial.pbori.get_var_mapping(P, x^2) + sage: sage.rings.polynomial.pbori.pbori.get_var_mapping(P, x^2) [None, x] """ my_names = list(ring._names) # we need .index(.) @@ -1837,7 +1837,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: M @@ -1846,7 +1846,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): sage: M.gens() (x, y) sage: type(M.gen(0)) - + Since :trac:`9138`, boolean monomial monoids are unique parents and are fit into the category framework:: @@ -1862,7 +1862,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): TESTS:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: B. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(B) sage: M @@ -1886,7 +1886,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: M # indirect doctest @@ -1900,7 +1900,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(P) sage: {M:1} # indirect doctest @@ -1914,7 +1914,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P = BooleanPolynomialRing(100, 'x') sage: M = BooleanMonomialMonoid(P) sage: M.ngens() @@ -1932,7 +1932,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.gen(0) @@ -1959,7 +1959,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.gens() @@ -1973,7 +1973,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M.get_action(ZZ) # indirect doctest @@ -1993,7 +1993,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x_monom = M(x); x_monom @@ -2004,7 +2004,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): Convert elements from :class:`BooleanMonomialMonoid` where the generators of self include the generators of the other monoid:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: N = BooleanMonomialMonoid(R) sage: m = M(N(y*z)); m @@ -2014,7 +2014,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): TESTS:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: N = BooleanMonomialMonoid(R) sage: M(N(y)) @@ -2024,7 +2024,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): ... ValueError: cannot convert monomial t to MonomialMonoid of Boolean PolynomialRing in x, y, z: name t not defined - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(4) sage: N = BooleanMonomialMonoid(R) sage: M(N(x*y*z)) @@ -2061,7 +2061,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x_monom = M(x); x_monom @@ -2200,7 +2200,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid, BooleanMonomial + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid, BooleanMonomial sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: BooleanMonomial(M) @@ -2215,7 +2215,7 @@ cdef class BooleanMonomial(MonoidElement): """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid, BooleanMonomial + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid, BooleanMonomial sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: BooleanMonomial(M) @@ -2235,7 +2235,7 @@ cdef class BooleanMonomial(MonoidElement): TESTS:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: R. = BooleanPolynomialRing(2) sage: M = BooleanMonomialMonoid(R) sage: t = M.0*M.1 @@ -2251,7 +2251,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x) < M(y) @@ -2280,7 +2280,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: sage: P. = BooleanPolynomialRing(2) - sage: M = sage.rings.polynomial.pbori.BooleanMonomialMonoid(P) + sage: M = sage.rings.polynomial.pbori.pbori.BooleanMonomialMonoid(P) sage: M(x*y) # indirect doctest x*y @@ -2427,7 +2427,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*y).deg() @@ -2455,7 +2455,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*y).degree() @@ -2560,7 +2560,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: len(M(x*y)) @@ -2574,7 +2574,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: list(M(x*z)) # indirect doctest @@ -2588,7 +2588,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: M(x*z).variables() # indirect doctest @@ -2602,7 +2602,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: list(M(x*z).iterindex()) @@ -2616,7 +2616,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y); z=M(z) @@ -2642,7 +2642,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y) @@ -2686,7 +2686,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y) @@ -2738,7 +2738,7 @@ cdef class BooleanMonomial(MonoidElement): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleSet + sage: from sage.rings.polynomial.pbori.pbori import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -2908,7 +2908,7 @@ cdef class BooleanPolynomial(MPolynomial): TESTS:: - sage: from sage.rings.polynomial.pbori import BooleanPolynomial + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomial sage: B. = BooleanPolynomialRing(3) sage: BooleanPolynomial(B) 0 @@ -4345,7 +4345,7 @@ cdef class BooleanPolynomial(MPolynomial): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleSet + sage: from sage.rings.polynomial.pbori.pbori import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -4706,7 +4706,7 @@ cdef class BooleanPolynomial(MPolynomial): ... TypeError: argument must be a BooleanPolynomial. """ - from sage.rings.polynomial.pbori import red_tail + from sage.rings.polynomial.pbori.pbori import red_tail if not I: return self if isinstance(I, BooleanPolynomialIdeal): @@ -4732,7 +4732,7 @@ cdef class PolynomialConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialConstruct().lead(a) a @@ -4746,7 +4746,7 @@ cdef class PolynomialConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialConstruct()(1, B) 1 @@ -4783,7 +4783,7 @@ cdef class MonomialConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: MonomialConstruct()(B) 1 @@ -4820,7 +4820,7 @@ cdef class VariableConstruct: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: VariableConstruct()(B) a @@ -5190,7 +5190,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): sage: I.reduce(gb[0]*B.gen(1)) 0 """ - from sage.rings.polynomial.pbori import red_tail + from sage.rings.polynomial.pbori.pbori import red_tail try: g = self.__gb except AttributeError: @@ -5318,7 +5318,7 @@ cdef class BooleSet: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleSet + sage: from sage.rings.polynomial.pbori.pbori import BooleSet sage: B. = BooleanPolynomialRing(4) sage: BS = BooleSet(a.set()) sage: BS @@ -5328,7 +5328,8 @@ cdef class BooleSet: sage: BS {{a,b}, {c}, {}} - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Monomial sage: BooleSet([Monomial(B)]) {{}} @@ -5375,7 +5376,7 @@ cdef class BooleSet: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleSet + sage: from sage.rings.polynomial.pbori.pbori import BooleSet sage: B. = BooleanPolynomialRing(4) sage: BS = BooleSet(B) sage: repr(BS) # indirect doctest @@ -5427,7 +5428,7 @@ cdef class BooleSet: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleSet + sage: from sage.rings.polynomial.pbori.pbori import BooleSet sage: B = BooleanPolynomialRing(5,'x') sage: x0,x1,x2,x3,x4 = B.gens() sage: f = x1*x2+x2*x3*x4+x2*x4+x3+x4+1 @@ -6033,7 +6034,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6054,7 +6055,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6077,7 +6078,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: list(iter(v)) @@ -6090,7 +6091,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector() sage: len(v) @@ -6106,7 +6107,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6136,7 +6137,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: l = [B.random_element() for _ in range(3)] sage: v = BooleanPolynomialVector(l) sage: len(v) @@ -6168,7 +6169,7 @@ cdef class BooleanPolynomialVector: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: v = BooleanPolynomialVector() sage: for i in range(5): ....: v.append(B.random_element()) @@ -6231,7 +6232,7 @@ cdef class ReductionStrategy: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: del red @@ -6249,7 +6250,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x) @@ -6279,7 +6280,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -6303,7 +6304,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -6328,7 +6329,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6350,7 +6351,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(a*b + c + 1) @@ -6374,7 +6375,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(a*b + c + 1) @@ -6414,7 +6415,7 @@ cdef class ReductionStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6476,7 +6477,7 @@ cdef class ReductionStrategy: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.opt_red_tail = True @@ -6513,7 +6514,7 @@ cdef class FGLMStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: x > y > z True @@ -6525,7 +6526,7 @@ cdef class FGLMStrategy: False sage: ideal = BooleanPolynomialVector([x+z, y+z]) sage: FGLMStrategy(old_ring, new_ring, ideal) - + Check that :trac:`13883` is fixed:: @@ -6561,7 +6562,7 @@ cdef class FGLMStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: ideal = BooleanPolynomialVector([x+z, y+z]) sage: list(ideal) @@ -6593,7 +6594,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: B. = BooleanPolynomialRing() sage: G = GroebnerStrategy(B) sage: H = GroebnerStrategy(G) @@ -6624,7 +6625,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_generator(a + b) @@ -6651,7 +6652,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_generator(a + b) @@ -6679,7 +6680,7 @@ cdef class GroebnerStrategy: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_as_you_wish(a + b) @@ -6738,7 +6739,7 @@ cdef class GroebnerStrategy: spanned by the generators but not in the set of generators:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6769,7 +6770,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6779,7 +6780,7 @@ cdef class GroebnerStrategy: sage: gb.add_generator(a*b + b + c*e + e + 1) sage: gb.add_generator(a + b + c*d + c*e + 1) - sage: from sage.rings.polynomial.pbori import BooleanPolynomialVector + sage: from sage.rings.polynomial.pbori.pbori import BooleanPolynomialVector sage: V= BooleanPolynomialVector([b*d, a*b]) sage: list(gb.faugere_step_dense(V)) [b + c*e + e + 1, c + d*f + e + f] @@ -6845,7 +6846,7 @@ cdef class GroebnerStrategy: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: gbs = GroebnerStrategy(B) sage: gbs.add_as_you_wish(a + b) @@ -6878,7 +6879,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: gb = GroebnerStrategy(B) sage: gb.add_generator(a*c + a*f + d*f + d + f) sage: gb.add_generator(b*c + b*e + c + d + 1) @@ -6918,7 +6919,7 @@ cdef class GroebnerStrategy: sage: I = B.ideal([B(f) for f in I.gens()]) sage: gb = I.groebner_basis() - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: G = GroebnerStrategy(B) sage: _ = [G.add_generator(f) for f in gb] @@ -6953,7 +6954,7 @@ cdef class GroebnerStrategy: sage: B. = BooleanPolynomialRing() sage: f = B.random_element() sage: g = B.random_element() - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: strat = GroebnerStrategy(B) sage: strat.add_generator(f) sage: strat.add_generator(g) @@ -6973,7 +6974,7 @@ cdef class GroebnerStrategy: EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import GroebnerStrategy + sage: from sage.rings.polynomial.pbori.pbori import GroebnerStrategy sage: G = GroebnerStrategy(B) sage: G.add_as_you_wish(a) @@ -7057,7 +7058,7 @@ cdef class BooleanMulAction(Action): """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleanMonomialMonoid + sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid sage: P. = BooleanPolynomialRing(3) sage: M = BooleanMonomialMonoid(P) sage: x = M(x); xy = M(x*y); z=M(z) @@ -7114,7 +7115,7 @@ def add_up_polynomials(BooleanPolynomialVector v, BooleanPolynomial init): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: v = BooleanPolynomialVector() sage: l = [B.random_element() for _ in range(5)] @@ -7143,7 +7144,7 @@ def red_tail(ReductionStrategy s, BooleanPolynomial p): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: red = ReductionStrategy(B) sage: red.add_generator(x + y + 1) @@ -7165,7 +7166,7 @@ def map_every_x_to_x_plus_one(BooleanPolynomial p): sage: B. = BooleanPolynomialRing(3) sage: f = a*b + z + 1; f a*b + z + 1 - sage: from sage.rings.polynomial.pbori import map_every_x_to_x_plus_one + sage: from sage.rings.polynomial.pbori.pbori import map_every_x_to_x_plus_one sage: map_every_x_to_x_plus_one(f) a*b + a + b + z + 1 sage: f(a+1,b+1,z+1) @@ -7201,7 +7202,7 @@ def zeros(pol, BooleSet s): This encodes the points (1,1,1,0), (1,1,0,0), (0,0,1,1) and (0,1,1,0). But of these only (1,1,0,0) evaluates to zero.:: - sage: from sage.rings.polynomial.pbori import zeros + sage: from sage.rings.polynomial.pbori.pbori import zeros sage: zeros(f,s) {{a,b}} @@ -7389,7 +7390,7 @@ def ll_red_nf_redsb(p, BooleSet reductors): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import ll_red_nf_redsb + sage: from sage.rings.polynomial.pbori.pbori import ll_red_nf_redsb sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7431,7 +7432,7 @@ def ll_red_nf_noredsb(BooleanPolynomial p, BooleSet reductors): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import ll_red_nf_noredsb + sage: from sage.rings.polynomial.pbori.pbori import ll_red_nf_noredsb sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7464,7 +7465,7 @@ def ll_red_nf_noredsb_single_recursive_call(BooleanPolynomial p, BooleSet reduct EXAMPLES:: - sage: from sage.rings.polynomial.pbori import ll_red_nf_noredsb_single_recursive_call + sage: from sage.rings.polynomial.pbori.pbori import ll_red_nf_noredsb_single_recursive_call sage: B. = BooleanPolynomialRing() sage: p = a*b + c + d + 1 sage: f,g = a + c + 1, b + d + 1 @@ -7505,7 +7506,7 @@ def if_then_else(root, a, b): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import if_then_else + sage: from sage.rings.polynomial.pbori.pbori import if_then_else sage: B = BooleanPolynomialRing(6,'x') sage: x0,x1,x2,x3,x4,x5 = B.gens() sage: f0 = x2*x3+x3 @@ -7584,7 +7585,7 @@ def top_index(s): EXAMPLES:: sage: B. = BooleanPolynomialRing(3) - sage: from sage.rings.polynomial.pbori import top_index + sage: from sage.rings.polynomial.pbori.pbori import top_index sage: top_index(x.lm()) 0 sage: top_index(y*z) @@ -7691,7 +7692,7 @@ def gauss_on_polys(inp): EXAMPLES:: sage: B. = BooleanPolynomialRing() - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: l = [B.random_element() for _ in range(B.ngens())] sage: A,v = Sequence(l,B).coefficient_matrix() sage: A @@ -7734,7 +7735,7 @@ def substitute_variables(BooleanPolynomialRing parent, vec, BooleanPolynomial po sage: B. = BooleanPolynomialRing() sage: f = a*b + c + 1 - sage: from sage.rings.polynomial.pbori import substitute_variables + sage: from sage.rings.polynomial.pbori.pbori import substitute_variables sage: substitute_variables(B, [a,b,c],f) a*b + c + 1 sage: substitute_variables(B, [a+1,b,c],f) @@ -7750,7 +7751,7 @@ def substitute_variables(BooleanPolynomialRing parent, vec, BooleanPolynomial po sage: f = a*b + c + 1 sage: B. = BooleanPolynomialRing(order='deglex') - sage: from sage.rings.polynomial.pbori import substitute_variables + sage: from sage.rings.polynomial.pbori.pbori import substitute_variables sage: substitute_variables(B, [x,y,z], f) * w w*x*y + w*z + w @@ -7772,7 +7773,7 @@ def set_random_seed(seed): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import random_set, set_random_seed + sage: from sage.rings.polynomial.pbori.pbori import random_set, set_random_seed sage: B. = BooleanPolynomialRing() sage: (a*b*c*d).lm() a*b*c*d @@ -7798,7 +7799,7 @@ def random_set(BooleanMonomial variables, length): EXAMPLES:: - sage: from sage.rings.polynomial.pbori import random_set, set_random_seed + sage: from sage.rings.polynomial.pbori.pbori import random_set, set_random_seed sage: B. = BooleanPolynomialRing() sage: (a*b*c*d).lm() a*b*c*d @@ -7873,7 +7874,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: [BooleConstant(i) for i in range(5)] [0, 1, 0, 1, 0] """ @@ -7883,7 +7884,7 @@ cdef class BooleConstant: """ EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: repr((BooleConstant(0),BooleConstant(1))) # indirect doctest '(0, 1)' @@ -7898,7 +7899,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: BooleConstant(0).deg() -1 sage: BooleConstant(1).deg() @@ -7912,7 +7913,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: BooleConstant(0).variables() () sage: BooleConstant(1).variables() @@ -7926,7 +7927,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: BooleConstant(0).is_one() False sage: BooleConstant(1).is_one() @@ -7940,7 +7941,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: BooleConstant(1).is_zero() False sage: BooleConstant(0).is_zero() @@ -7954,7 +7955,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: BooleConstant(1).is_constant() True sage: BooleConstant(0).is_constant() @@ -7968,7 +7969,7 @@ cdef class BooleConstant: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import BooleConstant + sage: from sage.rings.polynomial.pbori.pbori import BooleConstant sage: BooleConstant(1).has_constant_part() True sage: BooleConstant(0).has_constant_part() @@ -8017,7 +8018,7 @@ cdef class VariableFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: fac = VariableFactory() sage: fac = VariableFactory(B) @@ -8033,7 +8034,7 @@ cdef class VariableFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: VariableFactory()(B) a @@ -8066,7 +8067,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: fac = MonomialFactory() sage: fac = MonomialFactory(B) @@ -8079,7 +8080,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: MonomialFactory()(B) 1 @@ -8100,7 +8101,7 @@ cdef class MonomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: MonomialFactory()(B) 1 @@ -8145,7 +8146,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: fac = PolynomialFactory() @@ -8161,7 +8162,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialFactory().lead(a) a @@ -8175,7 +8176,7 @@ cdef class PolynomialFactory: EXAMPLES:: - sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.pbori import * sage: B. = BooleanPolynomialRing() sage: PolynomialFactory()(1, B) 1 From 59761821124227f84cc639a82b55d747f334b151 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 20 Aug 2020 16:41:52 +0900 Subject: [PATCH 260/379] Enable MathJax in TOC page --- src/doc/common/themes/sage/layout.html | 16 +- src/doc/common/themes/sageref/layout.html | 16 +- .../en/reference/arithmetic_curves/conf.py | 1 + .../en/reference/arithmetic_curves/index.rst | 121 +++++++++++++++ src/doc/en/reference/curves/index.rst | 120 --------------- src/doc/en/reference/euclidean_space/conf.py | 1 + .../en/reference/euclidean_space/index.rst | 11 ++ src/doc/en/reference/index.rst | 51 ++++--- src/doc/en/website/index.rst | 5 +- src/doc/en/website/templates/index.html | 5 +- src/sage_setup/docbuild/__init__.py | 142 +++++++++--------- 11 files changed, 254 insertions(+), 235 deletions(-) create mode 120000 src/doc/en/reference/arithmetic_curves/conf.py create mode 100644 src/doc/en/reference/arithmetic_curves/index.rst create mode 120000 src/doc/en/reference/euclidean_space/conf.py create mode 100644 src/doc/en/reference/euclidean_space/index.rst diff --git a/src/doc/common/themes/sage/layout.html b/src/doc/common/themes/sage/layout.html index 09844161a82..3f27fece8af 100644 --- a/src/doc/common/themes/sage/layout.html +++ b/src/doc/common/themes/sage/layout.html @@ -28,20 +28,16 @@ * sage.misc.html.math_parse() eats jQuery's dollar-sign shortcut. */ var jq = jQuery; jq(document).ready(function () { - var bar, bod, bg, fg, key, tog, wid_old, wid_new, resize, get_state, set_state; + var bar, bod, bg, fg, key, tog, wid_old, wid_new, get_state, set_state; bod = jq('div.bodywrapper'); bar = jq('div.sphinxsidebar'); tog = jq('
'); - /* Delayed resize helper. Not perfect but good enough. */ - resize = function () { - setTimeout(function () { - tog.height(bod.height()); - }, 100); - }; - jq(window).resize(function () { - resize(); + /* The sidebar toggle adapts its height to the bodywrapper height. */ + const resizeObserver = new ResizeObserver(entries => { + tog.height(bod.height()); }); + resizeObserver.observe(bod[0]); /* Setup and add the toggle. See Sphinx v0.5.1 default.css. */ fg = jq('div.sphinxsidebar p a').css('color') || 'rgb(152, 219, 204)'; @@ -58,7 +54,6 @@ .css('width', wid_new); bod.css('position', 'relative'); bod.prepend(tog); - resize(); /* Cookie helpers. */ key = 'sphinxsidebar='; @@ -99,7 +94,6 @@ bod.css('margin-left', wid_new); bod.addClass('wide'); } - resize(); }); /* Hide the normally visible sidebar? */ diff --git a/src/doc/common/themes/sageref/layout.html b/src/doc/common/themes/sageref/layout.html index 3a954d460c8..4d771933f32 100644 --- a/src/doc/common/themes/sageref/layout.html +++ b/src/doc/common/themes/sageref/layout.html @@ -29,20 +29,16 @@ * sage.misc.html.math_parse() eats jQuery's dollar-sign shortcut. */ var jq = jQuery; jq(document).ready(function () { - var bar, bod, bg, fg, key, tog, wid_old, wid_new, resize, get_state, set_state; + var bar, bod, bg, fg, key, tog, wid_old, wid_new, get_state, set_state; bod = jq('div.bodywrapper'); bar = jq('div.sphinxsidebar'); tog = jq('
'); - /* Delayed resize helper. Not perfect but good enough. */ - resize = function () { - setTimeout(function () { - tog.height(bod.height()); - }, 100); - }; - jq(window).resize(function () { - resize(); + /* The sidebar toggle adapts its height to the bodywrapper height. */ + const resizeObserver = new ResizeObserver(entries => { + tog.height(bod.height()); }); + resizeObserver.observe(bod[0]); /* Setup and add the toggle. See Sphinx v0.5.1 default.css. */ fg = jq('div.sphinxsidebar p a').css('color') || 'rgb(152, 219, 204)'; @@ -59,7 +55,6 @@ .css('width', wid_new); bod.css('position', 'relative'); bod.prepend(tog); - resize(); /* Cookie helpers. */ key = 'sphinxsidebar='; @@ -100,7 +95,6 @@ bod.css('margin-left', wid_new); bod.addClass('wide'); } - resize(); }); /* Hide the normally visible sidebar? */ diff --git a/src/doc/en/reference/arithmetic_curves/conf.py b/src/doc/en/reference/arithmetic_curves/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/arithmetic_curves/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/arithmetic_curves/index.rst b/src/doc/en/reference/arithmetic_curves/index.rst new file mode 100644 index 00000000000..94ce3cbf359 --- /dev/null +++ b/src/doc/en/reference/arithmetic_curves/index.rst @@ -0,0 +1,121 @@ +Elliptic curves +========================= + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/constructor + sage/schemes/elliptic_curves/jacobian + sage/schemes/elliptic_curves/ell_point + sage/schemes/elliptic_curves/ell_generic + sage/schemes/elliptic_curves/ell_field + sage/schemes/elliptic_curves/ell_finite_field + sage/schemes/elliptic_curves/formal_group + +Maps between them + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/weierstrass_morphism + sage/schemes/elliptic_curves/ell_curve_isogeny + sage/schemes/elliptic_curves/isogeny_small_degree + + +Elliptic curves over number fields +---------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/ell_rational_field + sage/schemes/elliptic_curves/ec_database + + sage/schemes/elliptic_curves/ell_number_field + sage/schemes/elliptic_curves/height + sage/schemes/elliptic_curves/saturation + + sage/schemes/elliptic_curves/ell_torsion + sage/schemes/elliptic_curves/gal_reps + sage/schemes/elliptic_curves/gal_reps_number_field + sage/schemes/elliptic_curves/isogeny_class + + sage/schemes/elliptic_curves/sha_tate + sage/schemes/elliptic_curves/cm + + +The following relate to elliptic curves over local nonarchimedean fields. + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/ell_local_data + sage/schemes/elliptic_curves/kodaira_symbol + sage/schemes/elliptic_curves/ell_tate_curve + +Analytic properties over `\CC`. + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/ell_wp + sage/schemes/elliptic_curves/period_lattice + sage/schemes/elliptic_curves/period_lattice_region + +Modularity and `L`-series over `\QQ`. + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/modular_parametrization + sage/schemes/elliptic_curves/ell_modular_symbols + sage/schemes/elliptic_curves/mod_sym_num + sage/schemes/elliptic_curves/lseries_ell + sage/schemes/elliptic_curves/heegner + sage/schemes/elliptic_curves/padic_lseries + +To be sorted +------------ + +.. toctree:: + :maxdepth: 2 + + sage/schemes/elliptic_curves/descent_two_isogeny + sage/schemes/elliptic_curves/ell_egros + sage/schemes/elliptic_curves/ell_padic_field + sage/schemes/elliptic_curves/gp_simon + sage/schemes/elliptic_curves/mod5family + sage/schemes/elliptic_curves/weierstrass_transform + +.. Not included because prove_BSD is bound in ell_rational_field, leading to duplicate citations +.. sage/schemes/elliptic_curves/BSD + +Hyperelliptic curves +==================== + +.. toctree:: + :maxdepth: 2 + + sage/schemes/hyperelliptic_curves/constructor + sage/schemes/hyperelliptic_curves/hyperelliptic_generic + sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field + sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field + sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field + + sage/schemes/hyperelliptic_curves/mestre + + sage/schemes/hyperelliptic_curves/monsky_washnitzer + sage/schemes/hyperelliptic_curves/hypellfrob + + sage/schemes/hyperelliptic_curves/jacobian_generic + sage/schemes/hyperelliptic_curves/jacobian_g2 + sage/schemes/hyperelliptic_curves/jacobian_homset + sage/schemes/hyperelliptic_curves/jacobian_morphism + + sage/schemes/hyperelliptic_curves/hyperelliptic_g2 + sage/schemes/hyperelliptic_curves/invariants + sage/schemes/hyperelliptic_curves/kummer_surface + + sage/interfaces/genus2reduction + +.. include:: ../footer.txt diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index fdaa33509bd..af3aad831d4 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -43,124 +43,4 @@ Riemann surfaces sage/schemes/riemann_surfaces/riemann_surface -Elliptic curves -========================= - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/constructor - sage/schemes/elliptic_curves/jacobian - sage/schemes/elliptic_curves/ell_point - sage/schemes/elliptic_curves/ell_generic - sage/schemes/elliptic_curves/ell_field - sage/schemes/elliptic_curves/ell_finite_field - sage/schemes/elliptic_curves/formal_group - -Maps between them - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/weierstrass_morphism - sage/schemes/elliptic_curves/ell_curve_isogeny - sage/schemes/elliptic_curves/isogeny_small_degree - - -Elliptic curves over number fields ----------------------------------- - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/ell_rational_field - sage/schemes/elliptic_curves/ec_database - - sage/schemes/elliptic_curves/ell_number_field - sage/schemes/elliptic_curves/height - sage/schemes/elliptic_curves/saturation - - sage/schemes/elliptic_curves/ell_torsion - sage/schemes/elliptic_curves/gal_reps - sage/schemes/elliptic_curves/gal_reps_number_field - sage/schemes/elliptic_curves/isogeny_class - - sage/schemes/elliptic_curves/sha_tate - sage/schemes/elliptic_curves/cm - - -The following relate to elliptic curves over local nonarchimedean fields. - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/ell_local_data - sage/schemes/elliptic_curves/kodaira_symbol - sage/schemes/elliptic_curves/ell_tate_curve - -Analytic properties over `\CC`. - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/ell_wp - sage/schemes/elliptic_curves/period_lattice - sage/schemes/elliptic_curves/period_lattice_region - -Modularity and `L`-series over `\QQ`. - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/modular_parametrization - sage/schemes/elliptic_curves/ell_modular_symbols - sage/schemes/elliptic_curves/mod_sym_num - sage/schemes/elliptic_curves/lseries_ell - sage/schemes/elliptic_curves/heegner - sage/schemes/elliptic_curves/padic_lseries - -To be sorted ------------- - -.. toctree:: - :maxdepth: 2 - - sage/schemes/elliptic_curves/descent_two_isogeny - sage/schemes/elliptic_curves/ell_egros - sage/schemes/elliptic_curves/ell_padic_field - sage/schemes/elliptic_curves/gp_simon - sage/schemes/elliptic_curves/mod5family - sage/schemes/elliptic_curves/weierstrass_transform - -.. Not included because prove_BSD is bound in ell_rational_field, leading to duplicate citations -.. sage/schemes/elliptic_curves/BSD - -Hyperelliptic curves -==================== - -.. toctree:: - :maxdepth: 2 - - sage/schemes/hyperelliptic_curves/constructor - sage/schemes/hyperelliptic_curves/hyperelliptic_generic - sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field - sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field - sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field - - sage/schemes/hyperelliptic_curves/mestre - - sage/schemes/hyperelliptic_curves/monsky_washnitzer - sage/schemes/hyperelliptic_curves/hypellfrob - - sage/schemes/hyperelliptic_curves/jacobian_generic - sage/schemes/hyperelliptic_curves/jacobian_g2 - sage/schemes/hyperelliptic_curves/jacobian_homset - sage/schemes/hyperelliptic_curves/jacobian_morphism - - sage/schemes/hyperelliptic_curves/hyperelliptic_g2 - sage/schemes/hyperelliptic_curves/invariants - sage/schemes/hyperelliptic_curves/kummer_surface - - sage/interfaces/genus2reduction - .. include:: ../footer.txt diff --git a/src/doc/en/reference/euclidean_space/conf.py b/src/doc/en/reference/euclidean_space/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/euclidean_space/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/euclidean_space/index.rst b/src/doc/en/reference/euclidean_space/index.rst new file mode 100644 index 00000000000..af4481f32f7 --- /dev/null +++ b/src/doc/en/reference/euclidean_space/index.rst @@ -0,0 +1,11 @@ +.. _euclidean-spaces: + +Euclidean Spaces and Vector Calculus +==================================== + +.. toctree:: + :maxdepth: 2 + + sage/manifolds/differentiable/euclidean + + sage/manifolds/operators diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 6deebf5e63d..e532066ea2f 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -1,19 +1,22 @@ .. _reference-manual: -************************************ -Welcome to the Sage Reference Manual -************************************ +********************* +Sage Reference Manual +********************* -This manual contains documentation for (almost) all of `Sage's -`_ features, each illustrated with examples -that are systematically tested with each release. A thematic index is -available below. +Welcome to the Sage reference manual. Here you find documentation for all of +`Sage `_'s features, illustrated with lots of examples. +A thematic index follows. -User Interface -============== +This documentation is licensed under the `Creative Commons Attribution-Share Alike 3.0 License`__:math:`.` -* :doc:`Command Line Interface (REPL) ` -* For the Jupyter notebook interface, visit `its documentation `_. +__ http://creativecommons.org/licenses/by-sa/3.0/ + +User Interfaces +=============== + +* :doc:`Command Line Interface ` +* `Jupyter Notebook Interface `_ Graphics ======== @@ -39,7 +42,7 @@ Basic Rings and Fields * :doc:`Commutative Polynomials ` * :doc:`Power Series and Laurent Series ` * :doc:`Finite Rings and Fields ` -* :doc:`p-Adic Numbers ` +* :doc:`\\(p\\)-adic Numbers ` * :doc:`Noncommutative Polynomials ` * :doc:`Quaternion Algebras ` @@ -93,7 +96,7 @@ Discrete Mathematics Geometry and Topology --------------------- -* :doc:`Euclidean Spaces and Vector Calculus ` +* :doc:`Euclidean Spaces and Vector Calculus ` * :doc:`Combinatorial and Discrete Geometry ` * :doc:`Cell Complexes and their Homology ` * :doc:`Manifolds and Differential Geometry ` @@ -113,8 +116,8 @@ Number Theory * :doc:`Diophantine approximation ` * :doc:`Quadratic Forms ` -* :doc:`L-Functions ` -* :doc:`Arithmetic Subgroups of SL_2(Z) ` +* :doc:`\\(L\\)-Functions ` +* :doc:`Arithmetic Subgroups of \\({\\rm SL}_2(\\ZZ)\\) ` * :doc:`General Hecke Algebras and Hecke Modules ` * :doc:`Modular Symbols ` * :doc:`Modular Forms ` @@ -125,7 +128,8 @@ Number Theory Algebraic and Arithmetic Geometry --------------------------------- * :doc:`Schemes ` -* :doc:`Plane, Elliptic and Hyperelliptic Curves ` +* :doc:`Plane and Space Curves ` +* :doc:`Elliptic and Hyperelliptic Curves ` Miscellaneous ------------- @@ -136,6 +140,9 @@ Miscellaneous Programming =========== +Facilities +---------- + * :doc:`Data Structures ` * :doc:`Utilities ` * :doc:`Test Framework ` @@ -146,19 +153,19 @@ Interfaces * :doc:`Interpreter Interfaces ` * :doc:`C/C++ Library Interfaces ` -* :doc:`Python technicalities ` +* :doc:`Python Technicalities ` General Information =================== * :doc:`External Packages ` -* :doc:`References ` +* :doc:`Bibliographic References ` * :doc:`History and License ` + +Indices and Tables +================== + * :ref:`genindex` * :ref:`modindex` * :ref:`search` -This work is licensed under a `Creative Commons Attribution-Share Alike -3.0 License`__. - -__ http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/src/doc/en/website/index.rst b/src/doc/en/website/index.rst index 5047eaa0f31..9db431db16e 100644 --- a/src/doc/en/website/index.rst +++ b/src/doc/en/website/index.rst @@ -1,5 +1,6 @@ Sage Documentation ================== -Do not edit this file as its output is overridden by the template in -templates/. +Do not edit this file as its output is overridden by the template in the templates directory. + +Do not delete this line as it enables MathJax in the webpage generated from this file: :math:`e^\pi+1=0`. diff --git a/src/doc/en/website/templates/index.html b/src/doc/en/website/templates/index.html index 4b94be6eb23..618fe51971a 100644 --- a/src/doc/en/website/templates/index.html +++ b/src/doc/en/website/templates/index.html @@ -253,8 +253,9 @@


- This document collects answers to some questions along - the line "How do I construct ... in Sage?" + This document collects answers to some questions along the line "How + do I construct ... in Sage?" Try to find out how to see the Riemann zeta + function \(\zeta(s)\) along the imaginary line \(s=\frac{1}{2}+it\).

diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index aee7d609a44..58e7fad3566 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -69,6 +69,7 @@ ########################################## # Parallel Building Ref Manual # ########################################## + def build_ref_doc(args): doc = args[0] lang = args[1] @@ -245,6 +246,21 @@ def pdf(self): self.latex() tex_dir = self._output_dir('latex') pdf_dir = self._output_dir('pdf') + + if self.name == 'reference': + # recover maths in tex, undoing what Sphinx did (trac #29993) + tex_file = os.path.join(tex_dir, 'reference.tex') + with open(tex_file) as f: + ref = f.read() + ref = re.sub(r'\\textbackslash{}', r'\\', ref) + ref = re.sub(r'\\textbackslash{}', r'\\', ref) + ref = re.sub(r'\\{', r'{', ref) + ref = re.sub(r'\\}', r'}', ref) + ref = re.sub(r'\\_', r'_', ref) + ref = re.sub(r'\\textasciicircum{}', r'^', ref) + with open(tex_file, 'w') as f: + f.write(ref) + make_target = "cd '%s' && $MAKE %s && mv -f *.pdf '%s'" error_message = "failed to run $MAKE %s in %s" command = 'all-pdf' @@ -272,6 +288,7 @@ def clean(self, *args): from .utils import build_many as _build_many + def build_many(target, args): """ Thin wrapper around `sage_setup.docbuild.utils.build_many` which uses the @@ -283,10 +300,10 @@ def build_many(target, args): if ABORT_ON_ERROR: raise - ########################################## # Parallel Building Ref Manual # ########################################## + def build_other_doc(args): document = args[0] name = args[1] @@ -295,6 +312,7 @@ def build_other_doc(args): logger.warning("\nBuilding %s.\n" % document) getattr(get_builder(document), name)(*args, **kwds) + class AllBuilder(object): """ A class used to build all of the documentation. @@ -386,20 +404,20 @@ def html(self): shutil.copytree(src, dst) else: shutil.copy2(src, dst) - self.create_html_redirects() def create_html_redirects(self): """ - Writes a number of small HTML files; these are files which - used to contain the main content of the reference manual - before before splitting the manual into multiple - documents. After the split, those files have moved, so in each - old location, write a file which redirects to the new version. - (This is so old URLs to pieces of the reference manual still + Writes a number of small HTML files; these are files which used to + contain the main content of the reference manual before splitting the + manual into multiple documents. After the split, those files have + moved, so in each old location, write a file which redirects to the new + version. (This is so old URLs to pieces of the reference manual still open the correct files.) """ - # The simple html template which will cause a redirect to the - # correct file + from sage.misc.superseded import deprecation + deprecation(29993, "This method was created in trac #6495 for backward compatibility. Not necessary anymore.") + + # The simple html template which will cause a redirect to the correct file. html_template = """ """ @@ -409,7 +427,7 @@ def create_html_redirects(self): reference_builder = ReferenceBuilder('reference') refdir = os.path.join(SAGE_DOC_SRC, 'en', 'reference') for document in reference_builder.get_all_documents(refdir): - #path is the directory above reference dir + # path is the directory above reference dir path = os.path.abspath(os.path.join(reference_dir, '..')) # the name of the subdocument @@ -427,7 +445,7 @@ def create_html_redirects(self): # a string like sage/algebras shorter_path = os.path.join(*short_path.split(os.sep)[2:]) - #Make the shorter path directory + # make the shorter path directory try: os.makedirs(os.path.join(reference_dir, shorter_path)) except OSError: @@ -448,6 +466,7 @@ def create_html_redirects(self): # write the html file which performs the redirect with open(redirect_filename, 'w') as f: + print(redirect_filename) f.write(html_template % redirect_url) @@ -563,33 +582,20 @@ def _wrapper(self, format, *args, **kwds): # of the PDF file. So we create an html file, based on # the file index.html from the "website" target. if format == 'pdf': - # First build the website page. (This only takes a - # few seconds.) + # First build the website page. This only takes a few seconds. getattr(get_builder('website'), 'html')() - # Copy the relevant pieces of - # SAGE_DOC/html/en/website/_static to output_dir. - # (Don't copy all of _static to save some space: we - # don't need all of the MathJax stuff, and in - # particular we don't need the fonts.) - website_dir = os.path.join(SAGE_DOC, 'html', - 'en', 'website') - static_files = ['COPYING.txt', 'basic.css', 'blank.gif', - 'default.css', 'doctools.js', 'favicon.ico', - 'file.png', 'jquery.js', 'minus.png', - 'pdf.png', 'plus.png', 'pygments.css', - 'sage.css', 'sageicon.png', - 'logo_sagemath.svg', 'logo_sagemath_black.svg', - 'searchtools.js', 'sidebar.js', 'underscore.js'] + + website_dir = os.path.join(SAGE_DOC, 'html', 'en', 'website') output_dir = self._output_dir(format, lang) - sage_makedirs(os.path.join(output_dir, '_static')) - for f in static_files: - try: - shutil.copyfile(os.path.join(website_dir, '_static', f), - os.path.join(output_dir, '_static', f)) - except IOError: # original file does not exist - pass - # Now modify website's index.html page and write it - # to output_dir. + + # Install in output_dir a symlink to the directory containing static files. + try: + os.symlink(os.path.join(website_dir, '_static'), os.path.join(output_dir, '_static')) + except FileExistsError: + pass + + # Now modify website's index.html page and write it to + # output_dir. with open(os.path.join(website_dir, 'index.html')) as f: html = f.read().replace('Documentation', 'Reference') html_output_dir = os.path.dirname(website_dir) @@ -598,53 +604,58 @@ def _wrapper(self, format, *args, **kwds): # From index.html, we want the preamble and the tail. html_end_preamble = html.find('

Sage Reference') html_bottom = html.rfind('') + len('') - # For the content, we modify doc/en/reference/index.rst, - # which has two parts: the body and the table of contents. + + # For the content, we modify doc/en/reference/index.rst, which + # has two parts: the body and the table of contents. with open(os.path.join(SAGE_DOC_SRC, lang, 'reference', 'index.rst')) as f: rst = f.read() - # Replace rst links with html links. There are two forms: + # Get rid of todolist and miscellaneous rst markup. + rst = rst.replace('.. _reference-manual:\n\n', '') + rst = re.sub(r'\\\\', r'\\', rst) + # Replace rst links with html links. There are three forms: # # `blah`__ followed by __ LINK # + # `blah `_ + # # :doc:`blah ` # - # Change the first form to + # Change the first and the second forms to # # blah # - # Change the second form to + # Change the third form to # # blah # - rst = re.sub(r'`([^`]*)`__\.\n\n__ (.*)', - r'\1.', rst) + rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)', + r'\1.', rst) + rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_', + r'\1', rst) rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`', - r'\1 ', - rst) - # Get rid of todolist and miscellaneous rst markup. - rst = rst.replace('.. toctree::', '') - rst = rst.replace(':maxdepth: 2', '') - rst = rst.replace('todolist', '') - start = rst.find('=\n') + 1 - end = rst.find('Table of Contents') + r'\1 ', rst) # Body: add paragraph

markup. + start = rst.rfind('*\n') + 1 + end = rst.find('\nUser Interfaces') rst_body = rst[start:end] rst_body = rst_body.replace('\n\n', '

\n

') - start = rst.find('Table of Contents') + 2*len('Table of Contents') + 1 - # Don't include the indices. + # TOC: don't include the indices + start = rst.find('\nUser Interfaces') end = rst.find('Indices and Tables') - # TOC: change * to

  • , change rst headers to html headers. rst_toc = rst[start:end] - rst_toc = rst_toc.replace('*', '
  • ') - rst_toc = re.sub('\n([A-Z][a-zA-Z, ]*)\n-*\n', - '\n\n\n

    \\1

    \n\n
      \n', rst_toc) - # Now write the file. + # change * to
    • ; change rst headers to html headers + rst_toc = re.sub(r'\*(.*)\n', + r'
    • \1
    • \n', rst_toc) + rst_toc = re.sub(r'\n([A-Z][a-zA-Z, ]*)\n[=]*\n', + r'
    \n\n\n

    \1

    \n\n
      \n', rst_toc) + rst_toc = re.sub(r'\n([A-Z][a-zA-Z, ]*)\n[-]*\n', + r'
    \n\n\n

    \1

    \n\n
      \n', rst_toc) + # now write the file. with open(os.path.join(output_dir, 'index.html'), 'w') as new_index: new_index.write(html[:html_end_preamble]) - new_index.write('

      ' + rst[:rst.find('\n')] + - ' (PDF version)'+ '

      ') + new_index.write('

      Sage Reference Manual (PDF version)'+ '

      ') new_index.write(rst_body) - new_index.write('

      Table of Contents

      \n\n
        ') + new_index.write('
          ') new_index.write(rst_toc) new_index.write('
        \n\n') new_index.write(html[html_bottom:]) @@ -1273,7 +1284,6 @@ def get_builder(name): print("of documents, or 'sage --docbuild --help' for more help.") sys.exit(1) - def format_columns(lst, align=u'<', cols=None, indent=4, pad=3, width=80): """ Utility function that formats a list as a simple table and returns @@ -1297,7 +1307,6 @@ def format_columns(lst, align=u'<', cols=None, indent=4, pad=3, width=80): s += u"\n" return s - def help_usage(s=u"", compact=False): """ Appends and returns a brief usage message for the Sage @@ -1419,7 +1428,6 @@ def help_message_long(option, opt_str, value, parser): print(f()) sys.exit(0) - def help_message_short(option=None, opt_str=None, value=None, parser=None, error=False): """ @@ -1437,7 +1445,6 @@ def help_message_short(option=None, opt_str=None, value=None, parser=None, parser.print_help() setattr(parser.values, 'printed_help', 1) - def help_wrapper(option, opt_str, value, parser): """ A helper wrapper for command-line options to the Sage @@ -1480,6 +1487,7 @@ def format_heading(self, heading): heading = "OPTIONS" return super(IndentedHelpFormatter2, self).format_heading(heading) + def setup_parser(): """ Sets up and returns a command-line OptionParser instance for the @@ -1568,7 +1576,6 @@ def setup_parser(): return parser - def setup_logger(verbose=1, color=True): r""" Set up a Python Logger instance for the Sage documentation builder. The @@ -1625,6 +1632,7 @@ def setup_logger(verbose=1, color=True): handler.setFormatter(formatter) logger.addHandler(handler) + class IntersphinxCache: """ Replace sphinx.ext.intersphinx.fetch_inventory by an in-memory From bc1f9e24bdd9125fc48ed8de59232eb09e950e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Aug 2020 22:06:21 +1200 Subject: [PATCH 261/379] fix more doctests --- src/sage/rings/polynomial/pbori/fglm.py | 18 ++++++++---------- src/sage/rings/polynomial/pbori/nf.py | 2 ++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index 210fe052e6a..02f1824318e 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -1,13 +1,3 @@ -if __name__ == "__main__": - import os - import sys - sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) - - - def _test(): - import doctest - doctest.testmod() - from .PyPolyBoRi import BooleSet, Polynomial, BoolePolynomialVector, \ FGLMStrategy, Monomial, Ring @@ -30,6 +20,7 @@ def fglm(I, from_ring, to_ring): TESTS:: + sage: from sage.rings.polynomial.pbori import * sage: from sage.rings.polynomial.pbori.PyPolyBoRi import OrderCode sage: dp_asc = OrderCode.dp_asc sage: r=declare_ring(['x','y','z'],dict()) @@ -37,6 +28,7 @@ def fglm(I, from_ring, to_ring): sage: new_ring = old_ring.clone(ordering=dp_asc) sage: (x,y,z) = [old_ring.variable(i) for i in range(3)] sage: ideal=[x+z, y+z]# lp Groebner basis + sage: from sage.rings.polynomial.pbori.fglm import fglm sage: list(fglm(ideal, old_ring, new_ring)) [y + x, z + x] """ @@ -52,12 +44,14 @@ def vars_real_divisors(monomial, monomial_set): TESTS:: + sage: from sage.rings.polynomial.pbori.pbori import * sage: from sage.rings.polynomial.pbori.PyPolyBoRi import OrderCode sage: dp_asc = OrderCode.dp_asc sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x = r.variable sage: b=BooleSet([x(1)*x(2),x(2)]) + sage: from sage.rings.polynomial.pbori.fglm import vars_real_divisors sage: vars_real_divisors(x(1)*x(2)*x(3),b) {{x(1),x(2)}} """ @@ -72,11 +66,15 @@ def m_k_plus_one(completed_elements, variables): TESTS:: + sage: from sage.rings.polynomial.pbori.pbori import * sage: from sage.rings.polynomial.pbori.PyPolyBoRi import OrderCode sage: dp_asc = OrderCode.dp_asc + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: x = r.variable + sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Monomial sage: s=BooleSet([x(1)*x(2),x(1),x(2),Monomial(r),x(3)]) + sage: from sage.rings.polynomial.pbori.fglm import m_k_plus_one sage: variables=BooleSet([x(1),x(2),x(3)]) sage: m_k_plus_one(s,variables) x(2)*x(3) diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index b15efac71ee..2235b340355 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -91,8 +91,10 @@ def multiply_polynomials(l, ring): TESTS:: + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(1000) sage: x=r.variable + sage: from sage.rings.polynomial.pbori.nf import multiply_polynomials sage: multiply_polynomials([x(3), x(2)+x(5)*x(6), x(0), x(0)+1], r) 0 """ From 5f6fb40362912ff5870e3e5f76abbd902243ee17 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 22 Jul 2020 09:27:21 +0200 Subject: [PATCH 262/379] #30393 wrap arb's experimental eigensolver --- src/sage/libs/arb/acb_mat.pxd | 12 +- src/sage/matrix/matrix_complex_ball_dense.pyx | 202 ++++++++++++++++++ 2 files changed, 213 insertions(+), 1 deletion(-) diff --git a/src/sage/libs/arb/acb_mat.pxd b/src/sage/libs/arb/acb_mat.pxd index 71f7e7f13f7..aff7fa7d7b2 100644 --- a/src/sage/libs/arb/acb_mat.pxd +++ b/src/sage/libs/arb/acb_mat.pxd @@ -1,6 +1,6 @@ # distutils: depends = acb_mat.h -from sage.libs.arb.types cimport acb_t, acb_mat_t, acb_poly_t +from sage.libs.arb.types cimport acb_t, acb_ptr, acb_srcptr, acb_mat_t, acb_poly_t, mag_t # acb_mat.h cdef extern from "arb_wrap.h": @@ -49,3 +49,13 @@ cdef extern from "arb_wrap.h": void acb_mat_exp_taylor_sum(acb_mat_t S, const acb_mat_t A, long N, long prec) void acb_mat_exp(acb_mat_t B, const acb_mat_t A, long prec) void acb_mat_trace(acb_t trace, const acb_mat_t mat, long prec) + void acb_mat_get_mid(acb_mat_t B, const acb_mat_t A) + void acb_mat_add_error_mag(acb_mat_t mat, const mag_t err) + int acb_mat_approx_eig_qr(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, const mag_t tol, long maxiter, long prec) + void acb_mat_eig_global_enclosure(mag_t eps, const acb_mat_t A, acb_srcptr E, const acb_mat_t R, long prec) + void acb_mat_eig_enclosure_rump(acb_t lam, acb_mat_t J, acb_mat_t R, const acb_mat_t A, const acb_t lambda_approx, const acb_mat_t R_approx, long prec) + int acb_mat_eig_simple_rump(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) + int acb_mat_eig_simple_vdhoeven_mourrain(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) + int acb_mat_eig_simple(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) + int acb_mat_eig_multiple_rump(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) + int acb_mat_eig_multiple(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx index d809009fe5e..dc0f8c6c346 100644 --- a/src/sage/matrix/matrix_complex_ball_dense.pyx +++ b/src/sage/matrix/matrix_complex_ball_dense.pyx @@ -50,7 +50,9 @@ from sage.rings.integer cimport Integer from sage.rings.polynomial.polynomial_complex_arb cimport Polynomial_complex_arb from sage.structure.element cimport Element, RingElement, Matrix from sage.structure.parent cimport Parent +from sage.structure.sequence import Sequence +from sage.misc.superseded import experimental from sage.rings.integer_ring import ZZ from sage.rings.polynomial import polynomial_ring_constructor @@ -648,6 +650,196 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): sig_off() return res + @experimental(trac_number=30393) + def eigenvalues(self, other=None, *, extend=None): + r""" + (Experimental.) Compute rigorous enclosures of the eigenvalues of this matrix. + + INPUT: + + - ``self`` -- an `n \times n` matrix + - ``other`` -- unsupported (generalized eigenvalue problem), should be ``None`` + - ``extend`` -- ignored + + OUTPUT: + + A :class:`~sage.structure.sequence.Sequence` of complex balls of + length equal to the size of the matrix. + + Each element represents one eigenvalue with the correct multiplicities + in case of overlap. The output intervals are either disjoint or + identical, and identical intervals are guaranteed to be grouped + consecutively. Each complete run of `k` identical balls thus represents + a cluster of exactly `k` eigenvalues which could not be separated from + each other at the current precision, but which could be isolated from + the other eigenvalues. + + There is currently no guarantee that the algorithm converges as the + working precision is increased. + + See the `Arb documentation `_ + for more information. + + EXAMPLES:: + + sage: from sage.matrix.benchmark import hilbert_matrix + sage: mat = hilbert_matrix(5).change_ring(CBF) + sage: mat.eigenvalues() + doctest:...: FutureWarning: This class/method/function is marked as experimental. + ... + [[1.567050691098...] + [+/- ...]*I, [0.208534218611...] + [+/- ...]*I, + [3.287928...e-6...] + [+/- ...]*I, [0.000305898040...] + [+/- ...]*I, + [0.011407491623...] + [+/- ...]*I] + + sage: mat = Permutation([2, 1, 4, 5, 3]).to_matrix().dense_matrix().change_ring(CBF) + sage: mat.eigenvalues() + Traceback (most recent call last): + ... + ValueError: unable to certify the eigenvalues + sage: precond = matrix(ZZ, [[-1, -2, 2, 2, -2], [2, -2, -2, -2, 2], + ....: [-2, 2, -1, 2, 1], [2, 1, -1, 0, 2], [-2, 0, 1, -1, 1]]) + sage: (~precond*mat*precond).eigenvalues() + [[-0.5000000000000...] + [-0.8660254037844...]*I, [-1.000000000000...] + [+/- ...]*I, + [-0.5000000000000...] + [0.8660254037844...]*I, + [1.000000000000...] + [+/- ...]*I, [1.000000000000...] + [+/- ...]*I] + + .. SEEALSO:: :meth:`eigenvectors_right` + """ + if self._nrows != self._ncols: + raise ValueError("self must be a square matrix") + cdef long n = self._ncols + cdef acb_ptr eigval_approx, eigval + cdef acb_mat_t eigvec_approx + if other is not None: + raise NotImplementedError + try: + eigval_approx = _acb_vec_init(n) + acb_mat_init(eigvec_approx, n, n) + acb_mat_approx_eig_qr(eigval_approx, NULL, eigvec_approx, self.value, NULL, 0, prec(self)) + eigval = _acb_vec_init(n) + if not acb_mat_eig_multiple(eigval, self.value, eigval_approx, eigvec_approx, prec(self)): + raise ValueError("unable to certify the eigenvalues") + res = _acb_vec_to_list(eigval, n, self._parent._base) + finally: + acb_mat_clear(eigvec_approx) + _acb_vec_clear(eigval, n) + _acb_vec_clear(eigval_approx, n) + return Sequence(res) + + @experimental(trac_number=30393) + def eigenvectors_right_approx(self, other=None, *, extend=None): + r""" + (Experimental.) Compute *non-rigorous* approximations of the + eigenvalues and eigenvectors of this matrix. + + INPUT: + + - ``self`` -- an `n \times n` matrix + - ``other`` -- unsupported (generalized eigenvalue problem), should be ``None`` + - ``extend`` -- ignored + + OUTPUT: + + A list of triples of the form ``(eigenvalue, [eigenvector], 1)``. The + eigenvalue and the entries of the eigenvector are complex balls with + zero radius. + + No guarantees are made about the accuracy of the output. + + See the `Arb documentation `_ + for more information. + + EXAMPLES:: + + sage: from sage.matrix.benchmark import hilbert_matrix + sage: mat = hilbert_matrix(3).change_ring(CBF) + sage: eigval, eigvec, _ = mat.eigenvectors_right_approx()[0] + doctest:...: FutureWarning: This class/method/function is marked as experimental. + ... + sage: eigval + [1.40831892712...] + sage: eigval.rad() + 0.00000000 + sage: eigvec + [([0.8270449269...], [-0.54744843072...], [-0.1276593297...])] + + .. SEEALSO:: :meth:`eigenvectors_right` + """ + if self._nrows != self._ncols: + raise ValueError("self must be a square matrix") + cdef long n = self._ncols + cdef Matrix_complex_ball_dense eigvec = self._new(n, n) + cdef acb_ptr _eigval + if other is not None: + raise NotImplementedError + try: + _eigval = _acb_vec_init(n) + acb_mat_approx_eig_qr(_eigval, NULL, eigvec.value, self.value, NULL, 0, prec(self)) + eigval = _acb_vec_to_list(_eigval, n, self._parent._base) + finally: + _acb_vec_clear(_eigval, n) + return [(l, [v], 1) for l, v in zip(eigval, eigvec)] + + @experimental(trac_number=30393) + def eigenvectors_right(self, other=None, *, extend=None): + r""" + (Experimental.) Compute rigorous enclosures of the eigenvalues and + eigenvectors of this matrix. + + INPUT: + + - ``self`` -- an `n \times n` matrix + - ``other`` -- unsupported (generalized eigenvalue problem), should be ``None`` + - ``extend`` -- ignored + + OUTPUT: + + A list of triples of the form ``(eigenvalue, [eigenvector], 1)``. + + Unlike :meth:`eigenvalues` and :meth:`eigenvectors_right_approx`, this + method currently fails in the presence of multiple eigenvalues. + + Additionally, there is currently no guarantee that the algorithm + converges as the working precision is increased. + + See the `Arb documentation `_ + for more information. + + EXAMPLES:: + + sage: from sage.matrix.benchmark import hilbert_matrix + sage: mat = hilbert_matrix(3).change_ring(CBF) + sage: mat.eigenvectors_right()[0] + doctest:...: FutureWarning: This class/method/function is marked as experimental. + ... + ([1.4083189271236...] + [+/- ...]*I, + [([0.82704492697...] + [+/- ...]*I, [-0.54744843072...] + [+/- ...]*I, [-0.12765932974...] + [+/- ...]*I)], + 1) + + .. SEEALSO:: :meth:`eigenvectors_right_approx`, :meth:`eigenvalues` + """ + if self._nrows != self._ncols: + raise ValueError("self must be a square matrix") + cdef long n = self._ncols + cdef acb_ptr eigval_approx, _eigval + cdef acb_mat_t eigvec_approx + cdef Matrix_complex_ball_dense eigvec = self._new(n, n) + if other is not None: + raise NotImplementedError + try: + _eigval = _acb_vec_init(n) + eigval_approx = _acb_vec_init(n) + acb_mat_init(eigvec_approx, n, n) + acb_mat_approx_eig_qr(eigval_approx, NULL, eigvec_approx, self.value, NULL, 0, prec(self)) + if not acb_mat_eig_simple(_eigval, NULL, eigvec.value, self.value, eigval_approx, eigvec_approx, prec(self)): + raise ValueError("unable to isolate the eigenvalues (multiple eigenvalues?)") + eigval = _acb_vec_to_list(_eigval, n, self._parent._base) + finally: + acb_mat_clear(eigvec_approx) + _acb_vec_clear(_eigval, n) + _acb_vec_clear(eigval_approx, n) + return [(l, [v], 1) for l, v in zip(eigval, eigvec)] + def exp(self): r""" Compute the exponential of this matrix. @@ -669,3 +861,13 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): acb_mat_exp(res.value, self.value, prec(self)) sig_off() return res + +cdef _acb_vec_to_list(acb_ptr vec, long n, Parent parent): + cdef ComplexBall b + res = [] + for i in range(n): + b = ComplexBall.__new__(ComplexBall) + b._parent = parent + acb_set(b.value, &vec[i]) + res.append(b) + return res From 4b12c2346a55a47861af170a3e4c671d2f86ec56 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 20 Aug 2020 13:58:58 +0200 Subject: [PATCH 263/379] added doctests; expanded folded graph to check antipodal; sketch antipodal implementations --- .../graphs/generators/distance_regular.pyx | 41 +++++++++++++++++++ src/sage/graphs/graph.py | 32 ++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 202700be9ff..83a67c00a00 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -604,3 +604,44 @@ def UstimenkoGraph(const int m, const int q): G.add_edges(edgesToAdd) G.name(f"Ustimenko graph ({m}, {q})") return G + +def antipodal_graph1(G): + r""" + Return the antipodal graph of `G` via straight-forward implementatiion + """ + from sage.graphs.distances_all_pairs import distances_all_pairs + + D = distances_all_pairs(G) + + # compute diameter + d = 0 + for u, v in itertools.combinations(G, 2): + if D[u][v] > d: + d = D[u][v] + + edges = [] + for u, v in itertools.combinations(G, 2): + if D[u][v] == d: + edges.append((u, v)) + + return Graph([G, edges], format="vertices_and_edges") + +def antipodal_graph2(G): + r""" + Return the antipodal graph of `G` via eccentricities + """ + from sage.graphs.distances_all_pairs import eccentricity + + ecc = eccentricity(G, algorithm="DHV") + + # diameter + diam = max(ecc) + + edges = [] + for u in G: + for v, d in G.breadth_first_search(u, report_distance=True): + if d == diam: + edges.append((u, v)) + + return Graph([G, edges], format="vertices_and_edges") + diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 54816b5c0fe..5a4073a9fef 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -9400,6 +9400,14 @@ def is_antipodal(self): True sage: graphs.CompleteGraph(5).is_antipodal() True + sage: G = Graph() + sage: G.is_antipodal() + Traceback (most recent call last): + ... + ValueError: diameter is not defined for the empty graph + sage: G = Graph(1) + sage: G.is_antipodal() + True """ G = self.distance_graph(self.diameter()) @@ -9418,7 +9426,7 @@ def is_antipodal(self): return True @doc_index("Leftovers") - def folded_graph(self): + def folded_graph(self, check=False): r""" Return the antipodal fold of this graph. @@ -9431,6 +9439,12 @@ def folded_graph(self): :meth:`sage.graphs.graph.is_antipodal` + INPUT: + + - ``check`` -- boolean (default: ``False``); whether to check if the + graph is antipodal. If ``check`` is ``True`` and the graph is not + antipodal, then return ``False``. + OUTPUT: This function returns a new graph and ``self`` is not touched. @@ -9459,6 +9473,8 @@ def folded_graph(self): False sage: G.folded_graph() # some garbage Folded Petersen graph: Graph on 2 vertices + sage: G.folded_graph(check=True) + False REFERENCES: @@ -9472,6 +9488,14 @@ def folded_graph(self): sage: G = graphs.CompleteGraph(5) sage: G.folded_graph() Folded Complete graph: Graph on 1 vertex + sage: G = Graph() + sage: G.folded_graph() + Traceback (most recent call last): + ... + ValueError: diameter is not defined for the empty graph + sage: G = Graph(1) + sage: G.folded_graph() + Folded Graph: Graph on 1 vertex """ G = self.distance_graph(self.diameter()) @@ -9481,6 +9505,12 @@ def folded_graph(self): while vertices: v = vertices.pop() clique = frozenset(G.neighbor_iterator(v, closed=True)) + + if check: + for u in clique: + if frozenset(G.neighbor_iterator(u, closed=True)) != clique: + return False + newVertices[numCliques] = clique numCliques += 1 vertices.difference_update(clique) From 243a0845540877ce3b9023f82bdbba76b90a13b9 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Thu, 20 Aug 2020 18:18:05 +0530 Subject: [PATCH 264/379] documentation remaining --- src/sage/graphs/base/boost_graph.pyx | 155 ++++++++++++++------------- 1 file changed, 83 insertions(+), 72 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 9861cd912d6..a1831633aaf 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2590,7 +2590,8 @@ cpdef diameter(G, algorithm=None, source=None, else: return LB -cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, algorithm=None): +cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, + weight_function=None, algorithm=None): r""" Compute the shortest paths to all vertices from each vertex in ``vertex_list``. @@ -2615,6 +2616,9 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al shortest paths from. By default (``None``), compute shortest paths from all vertices. + - ``order`` -- list (default: ``None``); order of vertices of `g` when + output is `dict of list`. By default (``None``), + - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of ``g`` are used, if ``g.weighted()==True``, otherwise all edges have @@ -2704,8 +2708,37 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al if v not in g: raise ValueError(f"the starting vertex {v} is not in the graph") + if order is not None: + if len(g) == len(order): + for v in g: + if v not in order: + raise ValueError("Given ordering is not a valid") + else: + raise ValueError("Given ordering is not a valid") + + cdef bint use_Bellman_Ford = algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost'] + if not use_Bellman_Ford: + # Check if there are edges with negative weights + if weight_function is not None: + for e in g.edges(sort=False): + if float(weight_function(e)) < 0: + use_Bellman_Ford = True + break + elif g.weighted(): + for _,_,w in g.edges(sort=False): + if float(w) < 0: + use_Bellman_Ford = True + break + + if algorithm in ['Dijkstra', 'Dijkstra_Boost']: + if use_Bellman_Ford: + raise RuntimeError("Dijkstra algorithm does not work with " + "negative weights, use Bellman-Ford instead") + elif algorithm is not None: + raise ValueError(f"unknown algorithm {algorithm!r}") + # These variables are automatically deleted when the function terminates. - cdef v_index vi, vert, pred + cdef v_index vi, v, vert, pred, w cdef list int_to_v = list(g) cdef dict v_to_int = {vv: vi for vi, vv in enumerate(g)} cdef result_distances result @@ -2717,86 +2750,64 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, weight_function=None, al else: boost_weighted_graph_from_sage_graph(&g_boost_und, g, v_to_int, weight_function) - if algorithm is None: - # Check if there are edges with negative weights - if weight_function is not None: - for e in g.edge_iterator(): - if float(weight_function(e)) < 0: - algorithm = 'Bellman-Ford' - break - elif g.weighted(): - for _,_,w in g.edge_iterator(): - if float(w) < 0: - algorithm = 'Bellman-Ford' - break - - if algorithm is None: - algorithm = 'Dijkstra' - - cdef list distances = [] - cdef list predecessors = [] + cdef dict distances = {} + cdef dict predecessors = {} for v in vertex_list: vi = v_to_int[v] - if g.is_directed(): - - if algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost']: + if use_Bellman_Ford: + if g.is_directed(): sig_on() result = g_boost_dir.bellman_ford_shortest_paths(vi) sig_off() - if not result.distances.size(): - raise ValueError("the graph contains a negative cycle") - elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: - try: - sig_on() - result = g_boost_dir.dijkstra_shortest_paths(vi) - sig_off() - if not result.distances.size(): - raise RuntimeError("Dijkstra algorithm does not " - "work with negative weights, " - "use Bellman-Ford instead") - except RuntimeError as msg: - raise RuntimeError(msg) else: - raise ValueError(f"unknown algorithm {algorithm!r}") - else: - if algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost']: sig_on() result = g_boost_und.bellman_ford_shortest_paths(vi) sig_off() - if not result.distances.size(): - raise ValueError("the graph contains a negative cycle") - - elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: - try: - sig_on() - result = g_boost_und.dijkstra_shortest_paths(vi) - sig_off() - if not result.distances.size(): - raise RuntimeError("Dijkstra algorithm does not " - "work with negative weights, " - "use Bellman-Ford instead") - except RuntimeError as msg: - raise RuntimeError(msg) - else: - raise ValueError(f"unknown algorithm {algorithm!r}") - - dist_v = [] - pred_v = [] - - for vert in range(g.num_verts()): - if result.distances[vert] != sys.float_info.max: - dist_v.append(result.distances[vert]) - pred = result.predecessors[vert] - if pred != vert: - pred_v.append(pred) - else: - pred_v.append(None) + if not result.distances.size(): + raise ValueError("the graph contains a negative cycle") + else: + if g.is_directed(): + sig_on() + result = g_boost_dir.dijkstra_shortest_paths(vi) + sig_off() else: - dist_v.append(+Infinity) - pred_v.append(None) - - distances.append(dist_v) - predecessors.append(pred_v) + sig_on() + result = g_boost_und.dijkstra_shortest_paths(vi) + sig_off() + if not result.distances.size(): + # This situation should never happen + raise RuntimeError("something goes wrong. Please report the " + "bug on sage-devel@googlegroups.com") + + if order is None: + cdef dict dist_v = {} + cdef dict pred_v = {} + + for vert in range(g.num_verts()): + if result.distances[vert] != sys.float_info.max: + w = int_to_v[vert] + dist_v[w] = result.distances[vert] + pred = result.predecessors[vert] + if pred == vert: + pred_v[w] = None + else: + pred_v[w] = int_to_v[pred] + else: + cdef list dict_v = [] + cdef list pred_v = [] + + for w in order: + vert = v_to_int[w] + if result.distances[vert] != sys.float_info.max: + dict_v.append(result.distances[vert]) + pred = result.predecessors[vert] + if pred == vert: + pred_v.append(None) + else: + pred_v.append(int_to_v[pred]) + + distances[v] = dist_v + predecessors[v] = pred_v return distances, predecessors From bd6466fde848fa807332601a9af359c640997478 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 20 Aug 2020 15:30:03 +0200 Subject: [PATCH 265/379] removed attepmts to antipodal_graph --- .../graphs/generators/distance_regular.pyx | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 83a67c00a00..202700be9ff 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -604,44 +604,3 @@ def UstimenkoGraph(const int m, const int q): G.add_edges(edgesToAdd) G.name(f"Ustimenko graph ({m}, {q})") return G - -def antipodal_graph1(G): - r""" - Return the antipodal graph of `G` via straight-forward implementatiion - """ - from sage.graphs.distances_all_pairs import distances_all_pairs - - D = distances_all_pairs(G) - - # compute diameter - d = 0 - for u, v in itertools.combinations(G, 2): - if D[u][v] > d: - d = D[u][v] - - edges = [] - for u, v in itertools.combinations(G, 2): - if D[u][v] == d: - edges.append((u, v)) - - return Graph([G, edges], format="vertices_and_edges") - -def antipodal_graph2(G): - r""" - Return the antipodal graph of `G` via eccentricities - """ - from sage.graphs.distances_all_pairs import eccentricity - - ecc = eccentricity(G, algorithm="DHV") - - # diameter - diam = max(ecc) - - edges = [] - for u in G: - for v, d in G.breadth_first_search(u, report_distance=True): - if d == diam: - edges.append((u, v)) - - return Graph([G, edges], format="vertices_and_edges") - From b549b0b206bf2d6824e6b12367a8bb23396a7832 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 20 Aug 2020 15:51:30 +0200 Subject: [PATCH 266/379] added placeholder antipodal_graph method; #30405 should change it --- src/sage/graphs/graph.py | 53 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 5a4073a9fef..4c07e9001d9 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -9409,7 +9409,7 @@ def is_antipodal(self): sage: G.is_antipodal() True """ - G = self.distance_graph(self.diameter()) + G = self.antipodal_graph() vertexSet = set(G) while vertexSet: @@ -9497,7 +9497,7 @@ def folded_graph(self, check=False): sage: G.folded_graph() Folded Graph: Graph on 1 vertex """ - G = self.distance_graph(self.diameter()) + G = self.antipodal_graph() vertices = set(G) newVertices = {} @@ -9527,6 +9527,55 @@ def folded_graph(self, check=False): H.name(f"Folded {name}") return H + def antipodal_graph(self): + r""" + Return the antipodal graph of ``self``. + + The antipodal graph of `G` has the same vertex set of `G` and two + vertices are adjacent if their distance in `G` is equal to the diameter + of `G`. + + OUTPUT: + + A new graph. ``self`` is not touched. + + EXAMPLES:: + + sage: G = graphs.JohnsonGraph(10, 5) + sage: G.antipodal_graph() + Antipodal graph of Johnson graph with parameters 10,5: Graph on 252 vertices + sage: G = graphs.HammingGraph(8, 2) + sage: G.antipodal_graph() + Antipodal graph of Hamming Graph with parameters 8,2: Graph on 256 vertices + + The antipodal graph of a disconnected graph is its complement:: + + sage: G = Graph(5) + sage: H = G.antipodal_graph() + sage: H.is_isomorphic(G.complement()) + True + + TESTS:: + + sage: G = Graph([(0, 1), (2, 3)]) + sage: H = G.antipodal_graph() + sage: H.is_isomorphic(Graph([(0, 2), (0, 3), (1, 2), (1, 3)])) + True + sage: G = Graph() + sage: G.antipodal_graph() + Traceback (most recent call last): + ... + ValueError: diameter is not defined for the empty graph + sage: G = Graph(1) + sage: G.antipodal_graph() + Antipodal graph of Graph: Looped graph on 1 vertex + """ + + H = self.distance_graph(self.diameter()) + + name = self.name() if self.name() != "" else "Graph" + H.name(f"Antipodal graph of {name}") + return H # Aliases to functions defined in other modules From f1f9aa49ab96364cf529e507dfc752431792c748 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 20 Aug 2020 16:04:26 +0200 Subject: [PATCH 267/379] fix docstring --- src/sage/graphs/graph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 4c07e9001d9..08e416c1e93 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -9527,6 +9527,7 @@ def folded_graph(self, check=False): H.name(f"Folded {name}") return H + @doc_index("Leftovers") def antipodal_graph(self): r""" Return the antipodal graph of ``self``. From 9ee8ea25217ca6b38e97f612d5cfc21743822e94 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Thu, 20 Aug 2020 20:20:26 +0530 Subject: [PATCH 268/379] introduced a extra parameter order --- src/sage/graphs/base/boost_graph.pyx | 86 +++++++++++++++++----------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index a1831633aaf..d97599ba8d2 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2616,8 +2616,7 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, shortest paths from. By default (``None``), compute shortest paths from all vertices. - - ``order`` -- list (default: ``None``); order of vertices of `g` when - output is `dict of list`. By default (``None``), + - ``order`` -- list (default: ``None``); order of vertices of `g`. - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of @@ -2636,11 +2635,19 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, OUTPUT: - A pair of dictionaries of dictionaries ``(distances, predecessors)`` such - that for each vertex ``v`` in ``vertex_list``, ``distances[v]`` store the - shortest distances of all the other vertices from ``v``, ``predecessors[v]`` - store the last vertices in the shortest path from ``v`` to all the other - vertices. + Two possible outputs: + + - A pair of dictionaries of list ``(distances, predecessors)``, when + ``order is not None``, such that for each vertex ``v`` in ``vertex_list``, + ``distances[v][i]`` store the shortest distance between ``v`` and + ``order[i]`` and ``predecessors[v][i]`` store the last vertex in the + shortest path from ``v`` to ``order[i]``. + + - A pair of dictionaries of dictionaries ``(distances, predecessors)`` such + that for each vertex ``v`` in ``vertex_list``, ``distances[v]`` store the + shortest distances of all the other vertices from ``v``, + ``predecessors[v]`` store the last vertices in the shortest path from + ``v`` to all the other vertices. EXAMPLES: @@ -2649,14 +2656,16 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, sage: from sage.graphs.base.boost_graph import shortest_paths_from_vertices sage: g = Graph([(0,1,1),(1,2,2),(1,3,4),(2,3,1)], weighted=True) sage: shortest_paths_from_vertices(g,[1,2]) - ({1: {0: 1, 1: 0, 2: 2, 3: 3}, 2: {0: 3, 1: 2, 2: 0, 3: 1}}, + ({1: {0: 1.0, 1: 0.0, 2: 2.0, 3: 3.0}, 2: {0: 3.0, 1: 2.0, 2: 0.0, 3: 1.0}}, {1: {0: 1, 1: None, 2: 1, 3: 2}, 2: {0: 1, 1: 2, 2: None, 3: 2}}) Directed graphs:: sage: g = DiGraph([(0,1,1),(1,2,-1),(2,0,2),(2,3,1)], weighted=True) sage: shortest_paths_from_vertices(g,1) - ({0: 1, 1: 0, 2: -1, 3: 0}, {0: 2, 1: None, 2: 1, 3: 2}) + ({1: {0: 1.0, 1: 0.0, 2: -1.0, 3: 0.0}}, {1: {0: 2, 1: None, 2: 1, 3: 2}}) + sage: shortest_paths_from_vertices(g, 1, [0,1,2,3]) + ({1: [1.0, 0.0, -1.0, 0.0]}, {1: [2, None, 1, 2]}) TESTS: @@ -2675,6 +2684,14 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, ... ValueError: the graph contains a negative cycle + If the given ordering is not valid:: + + sage: g = DiGraph([(0,1,1),(1,2,2),(2,0,0.5),(2,3,1)], weighted=True) + sage: shortest_paths_from_vertices(g,1,[0,1]) + Traceback (most recent call last): + ... + ValueError: Given ordering is not valid + If Dijkstra is used with negative weights:: sage: g = Graph([(0,1,1),(1,2,-2),(1,3,4)], weighted=True) @@ -2704,17 +2721,17 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, if not isinstance(vertex_list, list): vertex_list = [vertex_list] - for v in vertex_list: - if v not in g: - raise ValueError(f"the starting vertex {v} is not in the graph") + for vertex in vertex_list: + if vertex not in g: + raise ValueError(f"the starting vertex {vertex} is not in the graph") if order is not None: if len(g) == len(order): - for v in g: - if v not in order: - raise ValueError("Given ordering is not a valid") + for vertex in g: + if vertex not in order: + raise ValueError("Given ordering is not valid") else: - raise ValueError("Given ordering is not a valid") + raise ValueError("Given ordering is not valid") cdef bint use_Bellman_Ford = algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost'] if not use_Bellman_Ford: @@ -2725,8 +2742,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, use_Bellman_Ford = True break elif g.weighted(): - for _,_,w in g.edges(sort=False): - if float(w) < 0: + for _,_,wt in g.edges(sort=False): + if float(wt) < 0: use_Bellman_Ford = True break @@ -2744,14 +2761,16 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, cdef result_distances result cdef BoostVecWeightedDiGraphU g_boost_dir cdef BoostVecWeightedGraph g_boost_und + cdef dict dist_v_dict, pred_v_dict + cdef list dist_v_list, pred_v_list if g.is_directed(): boost_weighted_graph_from_sage_graph(&g_boost_dir, g, v_to_int, weight_function) else: boost_weighted_graph_from_sage_graph(&g_boost_und, g, v_to_int, weight_function) - cdef dict distances = {} - cdef dict predecessors = {} + distances = {} + predecessors = {} for v in vertex_list: vi = v_to_int[v] @@ -2781,33 +2800,36 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, "bug on sage-devel@googlegroups.com") if order is None: - cdef dict dist_v = {} - cdef dict pred_v = {} + dist_v_dict = {} + pred_v_dict = {} for vert in range(g.num_verts()): if result.distances[vert] != sys.float_info.max: w = int_to_v[vert] - dist_v[w] = result.distances[vert] + dist_v_dict[w] = result.distances[vert] pred = result.predecessors[vert] if pred == vert: - pred_v[w] = None + pred_v_dict[w] = None else: - pred_v[w] = int_to_v[pred] + pred_v_dict[w] = int_to_v[pred] + + distances[v] = dist_v_dict + predecessors[v] = pred_v_dict else: - cdef list dict_v = [] - cdef list pred_v = [] + dist_v_list = [] + pred_v_list = [] for w in order: vert = v_to_int[w] if result.distances[vert] != sys.float_info.max: - dict_v.append(result.distances[vert]) + dist_v_list.append(result.distances[vert]) pred = result.predecessors[vert] if pred == vert: - pred_v.append(None) + pred_v_list.append(None) else: - pred_v.append(int_to_v[pred]) + pred_v_list.append(int_to_v[pred]) - distances[v] = dist_v - predecessors[v] = pred_v + distances[v] = dist_v_list + predecessors[v] = pred_v_list return distances, predecessors From 2c27cb71aef080b571df934e06549959006b5494 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Thu, 20 Aug 2020 20:23:14 +0530 Subject: [PATCH 269/379] correct type method re-introduced for sake of doc-test. Will modify after finailizing --- src/sage/graphs/base/boost_graph.pyx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index d97599ba8d2..7b98e82c5d0 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -996,11 +996,22 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): dist = {} pred = {} + if weight_function is not None: + correct_type = type(weight_function(next(g.edge_iterator()))) + elif g.weighted(): + correct_type = type(next(g.edge_iterator())[2]) + else: + correct_type = int + # Needed for rational curves. + from sage.rings.real_mpfr import RealNumber, RR + if correct_type == RealNumber: + correct_type = RR + import sys for v in range(g.num_verts()): if result.distances[v] != sys.float_info.max: w = int_to_v[v] - dist[w] = result.distances[v] + dist[w] = correct_type(result.distances[v]) pred[w] = int_to_v[result.predecessors[v]] if result.predecessors[v] != v else None return (dist, pred) From 1cedc00fe789a4573c926cbff12e6fb9d2422c1e Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 20 Aug 2020 18:43:57 +0200 Subject: [PATCH 270/379] Add configuration of pycodestyle via tox --- src/tox.ini | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tox.ini b/src/tox.ini index 798c2f8cceb..478ca8beed2 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -23,3 +23,13 @@ passenv = HOME commands = {toxinidir}/../sage -t -p 0 {posargs:--all} + +[pycodestyle] +# Check for the following issues: +# E401: multiple imports on one line +# E701: multiple statements on one line (colon) +# E702: multiple statements on one line (semicolon) +# W605: invalid escape sequence ‘x’ +# See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes +select = E401,E701,E702,W605 +max-line-length = 160 \ No newline at end of file From a065a24d06ab0c67426c70da181899a33fe45125 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 23 Feb 2020 23:05:16 +0100 Subject: [PATCH 271/379] Update pexpect to 4.8.0 --- build/pkgs/pexpect/checksums.ini | 6 +- build/pkgs/pexpect/package-version.txt | 2 +- .../pexpect/patches/read_more_bytes.patch | 136 ------------------ 3 files changed, 4 insertions(+), 140 deletions(-) delete mode 100644 build/pkgs/pexpect/patches/read_more_bytes.patch diff --git a/build/pkgs/pexpect/checksums.ini b/build/pkgs/pexpect/checksums.ini index 73e23bebbe6..86e44f9e9f8 100644 --- a/build/pkgs/pexpect/checksums.ini +++ b/build/pkgs/pexpect/checksums.ini @@ -1,4 +1,4 @@ tarball=pexpect-VERSION.tar.gz -sha1=3d79bb7de5436cd0a8417a6249c765595a33abcf -md5=d4f3372965a996238d57d19b95d2e03a -cksum=2052391001 +sha1=3f6c41a4a72dc802f3431f5d2367a4259f9b1813 +md5=b260eb284efc19489466b804ff53c59b +cksum=966265148 diff --git a/build/pkgs/pexpect/package-version.txt b/build/pkgs/pexpect/package-version.txt index bc523389255..88f181192c4 100644 --- a/build/pkgs/pexpect/package-version.txt +++ b/build/pkgs/pexpect/package-version.txt @@ -1 +1 @@ -4.6.0.p0 +4.8.0 diff --git a/build/pkgs/pexpect/patches/read_more_bytes.patch b/build/pkgs/pexpect/patches/read_more_bytes.patch deleted file mode 100644 index 341bd2ca7a4..00000000000 --- a/build/pkgs/pexpect/patches/read_more_bytes.patch +++ /dev/null @@ -1,136 +0,0 @@ -See https://github.com/pexpect/pexpect/pull/304 - -commit 777ce20a819de8e0cd8ae1ea8e8e95440549f2c3 -Author: Jeroen Demeyer -Date: Fri Jun 29 15:13:22 2018 +0200 - - Read more bytes in read_nonblocking() - -diff --git a/pexpect/pty_spawn.py b/pexpect/pty_spawn.py -index e0e2b54..691c2c6 100644 ---- a/pexpect/pty_spawn.py -+++ b/pexpect/pty_spawn.py -@@ -430,61 +430,83 @@ class spawn(SpawnBase): - available right away then one character will be returned immediately. - It will not wait for 30 seconds for another 99 characters to come in. - -- This is a wrapper around os.read(). It uses select.select() to -- implement the timeout. ''' -+ On the other hand, if there are bytes available to read immediately, -+ all those bytes will be read (up to the buffer size). So, if the -+ buffer size is 1 megabyte and there is 1 megabyte of data available -+ to read, the buffer will be filled, regardless of timeout. -+ -+ This is a wrapper around os.read(). It uses select.select() or -+ select.poll() to implement the timeout. ''' - - if self.closed: - raise ValueError('I/O operation on closed file.') - -+ if self.use_poll: -+ def select(timeout): -+ return poll_ignore_interrupts([self.child_fd], timeout) -+ else: -+ def select(timeout): -+ return select_ignore_interrupts([self.child_fd], [], [], timeout)[0] -+ -+ # If there is data available to read right now, read as much as -+ # we can. We do this to increase performance if there are a lot -+ # of bytes to be read. This also avoids calling isalive() too -+ # often. See also: -+ # * https://github.com/pexpect/pexpect/pull/304 -+ # * http://trac.sagemath.org/ticket/10295 -+ if select(0): -+ try: -+ incoming = super(spawn, self).read_nonblocking(size) -+ except EOF: -+ # Maybe the child is dead: update some attributes in that case -+ self.isalive() -+ raise -+ while len(incoming) < size and select(0): -+ try: -+ incoming += super(spawn, self).read_nonblocking(size - len(incoming)) -+ except EOF: -+ # Maybe the child is dead: update some attributes in that case -+ self.isalive() -+ # Don't raise EOF, just return what we read so far. -+ return incoming -+ return incoming -+ - if timeout == -1: - timeout = self.timeout - -- # Note that some systems such as Solaris do not give an EOF when -- # the child dies. In fact, you can still try to read -- # from the child_fd -- it will block forever or until TIMEOUT. -- # For this case, I test isalive() before doing any reading. -- # If isalive() is false, then I pretend that this is the same as EOF. - if not self.isalive(): -- # timeout of 0 means "poll" -- if self.use_poll: -- r = poll_ignore_interrupts([self.child_fd], timeout) -- else: -- r, w, e = select_ignore_interrupts([self.child_fd], [], [], 0) -- if not r: -- self.flag_eof = True -- raise EOF('End Of File (EOF). Braindead platform.') -+ # The process is dead, but there may or may not be data -+ # available to read. Note that some systems such as Solaris -+ # do not give an EOF when the child dies. In fact, you can -+ # still try to read from the child_fd -- it will block -+ # forever or until TIMEOUT. For that reason, it's important -+ # to do this check before calling select() with timeout. -+ if select(0): -+ return super(spawn, self).read_nonblocking(size) -+ self.flag_eof = True -+ raise EOF('End Of File (EOF). Braindead platform.') - elif self.__irix_hack: - # Irix takes a long time before it realizes a child was terminated. -+ # Make sure that the timeout is at least 2 seconds. - # FIXME So does this mean Irix systems are forced to always have - # FIXME a 2 second delay when calling read_nonblocking? That sucks. -- if self.use_poll: -- r = poll_ignore_interrupts([self.child_fd], timeout) -- else: -- r, w, e = select_ignore_interrupts([self.child_fd], [], [], 2) -- if not r and not self.isalive(): -- self.flag_eof = True -- raise EOF('End Of File (EOF). Slow platform.') -- if self.use_poll: -- r = poll_ignore_interrupts([self.child_fd], timeout) -- else: -- r, w, e = select_ignore_interrupts( -- [self.child_fd], [], [], timeout -- ) -- -- if not r: -- if not self.isalive(): -- # Some platforms, such as Irix, will claim that their -- # processes are alive; timeout on the select; and -- # then finally admit that they are not alive. -- self.flag_eof = True -- raise EOF('End of File (EOF). Very slow platform.') -- else: -- raise TIMEOUT('Timeout exceeded.') -+ if timeout is not None and timeout < 2: -+ timeout = 2 - -- if self.child_fd in r: -+ # Because of the select(0) check above, we know that no data -+ # is available right now. But if a non-zero timeout is given -+ # (possibly timeout=None), we call select() with a timeout. -+ if (timeout != 0) and select(timeout): - return super(spawn, self).read_nonblocking(size) - -- raise ExceptionPexpect('Reached an unexpected state.') # pragma: no cover -+ if not self.isalive(): -+ # Some platforms, such as Irix, will claim that their -+ # processes are alive; timeout on the select; and -+ # then finally admit that they are not alive. -+ self.flag_eof = True -+ raise EOF('End of File (EOF). Very slow platform.') -+ else: -+ raise TIMEOUT('Timeout exceeded.') - - def write(self, s): - '''This is similar to send() except that there is no return value. From 4b131b0990f66efa5742b0960f8287f861d98c26 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 23 Feb 2020 23:05:30 +0100 Subject: [PATCH 272/379] Adapt expect_upto and expect_peek to pexpect 4.8 changes --- src/sage/interfaces/sagespawn.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/sagespawn.pyx b/src/sage/interfaces/sagespawn.pyx index 8da000ffd12..8644b080298 100644 --- a/src/sage/interfaces/sagespawn.pyx +++ b/src/sage/interfaces/sagespawn.pyx @@ -149,7 +149,8 @@ class SageSpawn(spawn): u'hello world\r\n' """ ret = self.expect(*args, **kwds) - self.buffer = self.before + self.after + self.buffer + self._before = self.buffer_type() + self._before.write(self.before + self.after + self.buffer) return ret def expect_upto(self, *args, **kwds): @@ -167,7 +168,8 @@ class SageSpawn(spawn): u'world\r\n' """ ret = self.expect(*args, **kwds) - self.buffer = self.after + self.buffer + self._before = self.buffer_type() + self._before.write(self.after + self.buffer) return ret From 97a4b082adc8b1ebb8cf80fbd6f70670a34b599b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Thu, 20 Aug 2020 18:42:57 +0200 Subject: [PATCH 273/379] 29240: pexpect upstream_url --- build/pkgs/pexpect/checksums.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/pexpect/checksums.ini b/build/pkgs/pexpect/checksums.ini index 86e44f9e9f8..d973aacf784 100644 --- a/build/pkgs/pexpect/checksums.ini +++ b/build/pkgs/pexpect/checksums.ini @@ -2,3 +2,4 @@ tarball=pexpect-VERSION.tar.gz sha1=3f6c41a4a72dc802f3431f5d2367a4259f9b1813 md5=b260eb284efc19489466b804ff53c59b cksum=966265148 +upstream_url=https://codeload.github.com/pexpect/pexpect/tar.gz/VERSION From eeffa98eb786ae255585238547ece941128ba86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Thu, 20 Aug 2020 00:11:12 +0200 Subject: [PATCH 274/379] t-30338: upgrade: libhomfly 1.02r6 --- build/pkgs/libhomfly/checksums.ini | 7 ++++--- build/pkgs/libhomfly/distros/debian.txt | 1 + build/pkgs/libhomfly/distros/fedora.txt | 1 + build/pkgs/libhomfly/distros/freebsd.txt | 1 + build/pkgs/libhomfly/distros/gentoo.txt | 1 + build/pkgs/libhomfly/distros/nix.txt | 1 + build/pkgs/libhomfly/package-version.txt | 2 +- 7 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 build/pkgs/libhomfly/distros/debian.txt create mode 100644 build/pkgs/libhomfly/distros/fedora.txt create mode 100644 build/pkgs/libhomfly/distros/freebsd.txt create mode 100644 build/pkgs/libhomfly/distros/gentoo.txt create mode 100644 build/pkgs/libhomfly/distros/nix.txt diff --git a/build/pkgs/libhomfly/checksums.ini b/build/pkgs/libhomfly/checksums.ini index 724ee70e144..0d3a841d93c 100644 --- a/build/pkgs/libhomfly/checksums.ini +++ b/build/pkgs/libhomfly/checksums.ini @@ -1,4 +1,5 @@ tarball=libhomfly-VERSION.tar.gz -sha1=552da4595fbc84fc5744233b0ab4fdf824c85371 -md5=7cb0cb17d53267f78a09d0a10f653918 -cksum=443005919 +sha1=a8a4d9fb5bfacdbacab32bb0c8ea6d21ad9f7e54 +md5=1bb639fd4182be7ac9dbec0c3e631ce2 +cksum=920200221 +upstream_url=https://github.com/miguelmarco/libhomfly/releases/download/VERSION/libhomfly-VERSION.tar.gz diff --git a/build/pkgs/libhomfly/distros/debian.txt b/build/pkgs/libhomfly/distros/debian.txt new file mode 100644 index 00000000000..4da5d0c664a --- /dev/null +++ b/build/pkgs/libhomfly/distros/debian.txt @@ -0,0 +1 @@ +libhomfly diff --git a/build/pkgs/libhomfly/distros/fedora.txt b/build/pkgs/libhomfly/distros/fedora.txt new file mode 100644 index 00000000000..4da5d0c664a --- /dev/null +++ b/build/pkgs/libhomfly/distros/fedora.txt @@ -0,0 +1 @@ +libhomfly diff --git a/build/pkgs/libhomfly/distros/freebsd.txt b/build/pkgs/libhomfly/distros/freebsd.txt new file mode 100644 index 00000000000..bdb2dac7829 --- /dev/null +++ b/build/pkgs/libhomfly/distros/freebsd.txt @@ -0,0 +1 @@ +math/libhomfly diff --git a/build/pkgs/libhomfly/distros/gentoo.txt b/build/pkgs/libhomfly/distros/gentoo.txt new file mode 100644 index 00000000000..4cbcd781ad6 --- /dev/null +++ b/build/pkgs/libhomfly/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/libhomfly diff --git a/build/pkgs/libhomfly/distros/nix.txt b/build/pkgs/libhomfly/distros/nix.txt new file mode 100644 index 00000000000..4da5d0c664a --- /dev/null +++ b/build/pkgs/libhomfly/distros/nix.txt @@ -0,0 +1 @@ +libhomfly diff --git a/build/pkgs/libhomfly/package-version.txt b/build/pkgs/libhomfly/package-version.txt index 4f5e942bb7d..655a558106e 100644 --- a/build/pkgs/libhomfly/package-version.txt +++ b/build/pkgs/libhomfly/package-version.txt @@ -1 +1 @@ -1.02r4.p0 +1.02r6 From dde3b4f3955605d27e30f488c6dc11157031b720 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 Aug 2020 13:22:05 -0700 Subject: [PATCH 275/379] src/tox.ini: Add testenv:pycodestyle --- src/tox.ini | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/tox.ini b/src/tox.ini index 478ca8beed2..4b400740739 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -1,18 +1,27 @@ -## If the system python has tox installed, you can use it to run -## the sage doctests. From the SAGE_ROOT/src directory: +## Configuration for tox. +## Needs tox installed in the system python. ## -## $ tox +## doctest: Run the sage doctests. From the SAGE_ROOT/src directory: ## -## Arguments are passed on to "sage -t": +## $ tox ## -## $ tox sage/geometry +## Arguments are passed on to "sage -t": ## -## To pass on options to "sage -t", use -- to separate it from tox options: +## $ tox sage/geometry ## -## $ tox -- --verbose --optional=sage,pynormaliz --long sage/geometry +## To pass on options to "sage -t", use -- to separate it from tox options: +## +## $ tox -- --verbose --optional=sage,pynormaliz --long sage/geometry +## +## pycodestyle: +## +## $ tox -e pycodestyle +## +## Note that on the first run, tox automatically installs pycodestyle +## in a virtual environment. ## [tox] -envlist = doctest +envlist = doctest, pycodestyle skipsdist = true [testenv:doctest] @@ -24,6 +33,10 @@ passenv = commands = {toxinidir}/../sage -t -p 0 {posargs:--all} +[testenv:pycodestyle] +deps = pycodestyle +commands = pycodestyle {posargs:sage/} + [pycodestyle] # Check for the following issues: # E401: multiple imports on one line From 939e9e0e08d7d5c4e4c073a571ca8e4aac22ef77 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 Aug 2020 13:27:12 -0700 Subject: [PATCH 276/379] src/tox.ini: Better default arg testenv:pycodestyle --- src/tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tox.ini b/src/tox.ini index 4b400740739..f446634717a 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -35,7 +35,7 @@ commands = [testenv:pycodestyle] deps = pycodestyle -commands = pycodestyle {posargs:sage/} +commands = pycodestyle {posargs:{toxinidir}/sage/} [pycodestyle] # Check for the following issues: From b513981b875e25aa9b681c932fcfed9cec8506dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 21 Aug 2020 10:43:34 +1200 Subject: [PATCH 277/379] more doctests fixes --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 3 ++- src/sage/rings/polynomial/pbori/ll.py | 16 ++++++++++++---- src/sage/rings/polynomial/pbori/randompoly.py | 8 ++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index a5e1bf94b20..e5fcb0616af 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -10,7 +10,8 @@ EXAMPLES:: - sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.pbori import * + sage: from sage.rings.polynomial.pbori.blocks import declare_ring sage: r=declare_ring(["x0","x1","x2","y0","y1","y2"], globals()) sage: x0>x1 True diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index 5135fe5033c..846563ee5f2 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -212,9 +212,11 @@ class RingMap(object): TESTS:: - sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.pbori import * + sage: from sage.rings.polynomial.pbori.blocks import declare_ring, Block sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: from sage.rings.polynomial.pbori.ll import RingMap sage: mapping = RingMap(to_ring, from_ring) sage: (x(1)+1).navigation().value() 6 @@ -238,9 +240,11 @@ def __init__(self, to_ring, from_ring): TESTS:: - sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.pbori import * + sage: from sage.rings.polynomial.pbori.blocks import declare_ring, Block sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: from sage.rings.polynomial.pbori.ll import RingMap sage: mapping = RingMap(to_ring, from_ring) sage: mapping(x(1)+1) x(1) + 1 @@ -277,9 +281,11 @@ def __call__(self, poly): TESTS:: - sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.pbori import * + sage: from sage.rings.polynomial.pbori.blocks import declare_ring, Block sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: from sage.rings.polynomial.pbori.ll import RingMap sage: mapping = RingMap(to_ring, from_ring) sage: mapping(x(1)+1) x(1) + 1 @@ -290,9 +296,11 @@ def invert(self, poly): r""" Inverted map to initial ring. - sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.pbori import * + sage: from sage.rings.polynomial.pbori.blocks import declare_ring, Block sage: to_ring = declare_ring([Block("x", 10)], globals()) sage: from_ring = declare_ring([Block("y", 5), Block("x", 10)], globals()) + sage: from sage.rings.polynomial.pbori.ll import RingMap sage: mapping = RingMap(to_ring, from_ring) sage: mapping.invert(mapping(x(1)+1)) x(1) + 1 diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index 5c5f5de473b..b06d2ffce56 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -42,11 +42,12 @@ def sparse_random_system(ring, number_of_polynomials, sage: from sage.rings.polynomial.pbori import * sage: r=Ring(10) + sage: from sage.rings.polynomial.pbori.randompoly import sparse_random_system sage: s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123) sage: [p.deg() for p in s] [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] sage: sorted(groebner_basis(s), reverse=True) - [x(0), x(1), x(2), x(3), x(4) + 1, x(5), x(6) + 1, x(7), x(8) + 1, x(9)] + [x(0), x(1) + 1, x(2), x(3) + 1, x(4) + 1, x(5), x(6), x(7) + 1, x(8) + 1, x(9) + 1] """ if random_seed is not None: set_random_seed(random_seed) @@ -79,8 +80,11 @@ def sparse_random_system_data_file_content( TESTS:: + + sage: from sage.rings.polynomial.pbori import * + sage: from sage.rings.polynomial.pbori.randompoly import sparse_random_system_data_file_content sage: sparse_random_system_data_file_content(10, number_of_polynomials = 5, variables_per_polynomial = 3, degree=2, random_seed=123) - declare_ring(['x'+str(i) for in range(10)])\nideal=\\\n[...]\n\n + "declare_ring(['x'+str(i) for in range(10)])\nideal=\\\n[...]\n\n" """ dummy_dict = dict() r = declare_ring(['x' + str(i) for i in range(number_of_variables)], From 1cf36c7a8e97fc7242ddec48a7678e1b9ba8b502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 21 Aug 2020 10:43:56 +1200 Subject: [PATCH 278/379] Remove VariableFactory from the lazy_import list for now. --- src/sage/rings/polynomial/pbori/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/polynomial/pbori/__init__.py b/src/sage/rings/polynomial/pbori/__init__.py index 834c1c1b728..be0cb9c6411 100644 --- a/src/sage/rings/polynomial/pbori/__init__.py +++ b/src/sage/rings/polynomial/pbori/__init__.py @@ -76,7 +76,6 @@ def plist(a, b): 'TermOrder_from_pb_order', 'VariableBlock', 'VariableConstruct', - 'VariableFactory', 'add_up_polynomials', 'block_dlex', 'block_dp_asc', From 64c57d2bc82f3920d8c91ca06669d5e4bb14df48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 21 Aug 2020 11:06:14 +1200 Subject: [PATCH 279/379] Last bunch of doctest fixes --- .../rings/polynomial/pbori/easy_polynomials.py | 6 ++++-- src/sage/rings/polynomial/pbori/frontend.py | 2 +- src/sage/rings/polynomial/pbori/gbcore.py | 3 +++ src/sage/rings/polynomial/pbori/parallel.py | 14 ++++---------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py index 96a16692e15..a5ee335b98b 100644 --- a/src/sage/rings/polynomial/pbori/easy_polynomials.py +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -7,8 +7,9 @@ def easy_linear_polynomials(p): Get linear polynomials implied by given polynomial. TESTS:: - + sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.easy_polynomials import easy_linear_polynomials sage: easy_linear_polynomials(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] sage: easy_linear_polynomials(x(1)*x(2) + 0) @@ -32,8 +33,9 @@ def easy_linear_polynomials_via_interpolation(p): Get linear polynomials implied by given polynomial using interpolation of the variety. TESTS:: - + sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.easy_polynomials import easy_linear_polynomials_via_interpolation sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 1) [x(1) + 1, x(2) + 1] sage: easy_linear_polynomials_via_interpolation(x(1)*x(2) + 0) diff --git a/src/sage/rings/polynomial/pbori/frontend.py b/src/sage/rings/polynomial/pbori/frontend.py index 8da780e3218..7c0fc80a3de 100644 --- a/src/sage/rings/polynomial/pbori/frontend.py +++ b/src/sage/rings/polynomial/pbori/frontend.py @@ -5,6 +5,7 @@ EXAMPLES:: + sage: from sage.rings.polynomial.pbori.frontend import * sage: x(0) x(0) sage: x(0)*x(0) @@ -33,7 +34,6 @@ from . import * from .blocks import declare_ring as orig_declare_ring -from os import environ as env, path as os_path def block_scheme_names(blocks): diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index 4c1458624fe..c690d4dcf25 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -339,6 +339,8 @@ def ll_constants_pre(I): def variety_size_from_gb(I): """ + sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.gbcore import variety_size_from_gb sage: r=Ring(100) sage: x = r.variable sage: variety_size_from_gb([]) @@ -390,6 +392,7 @@ def other_ordering_pre(I, option_set, kwds): sage: from sage.rings.polynomial.pbori.blocks import declare_ring sage: r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) sage: id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] + sage: from sage.rings.polynomial.pbori.gbcore import groebner_basis sage: groebner_basis(id) [1] diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 07f635448d1..944a0212854 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -45,7 +45,8 @@ def to_fast_pickable(l): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring + sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.parallel import to_fast_pickable, from_fast_pickable sage: r=Ring(1000) sage: x=r.variable sage: to_fast_pickable([Polynomial(1, r)]) @@ -121,7 +122,8 @@ def from_fast_pickable(l, r): EXAMPLES:: - sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring + sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.parallel import from_fast_pickable sage: r=Ring(1000) sage: x = r.variable sage: from_fast_pickable([[1], []], r) @@ -296,11 +298,3 @@ def groebner_basis_first_finished(I, *l): pool.terminate() return res - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() From 878d941065c696fc38d9ec59d917a0ca1e3375da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 21 Aug 2020 11:08:49 +1200 Subject: [PATCH 280/379] Remove the sage_brial package since its meaningful content is merged. --- build/pkgs/sage_brial/SPKG.rst | 24 ------------------- build/pkgs/sage_brial/checksums.ini | 5 ---- build/pkgs/sage_brial/dependencies | 5 ---- build/pkgs/sage_brial/package-version.txt | 1 - build/pkgs/sage_brial/spkg-install.in | 7 ------ .../pkgs/sage_brial/spkg-legacy-uninstall.in | 2 -- build/pkgs/sage_brial/type | 1 - 7 files changed, 45 deletions(-) delete mode 100644 build/pkgs/sage_brial/SPKG.rst delete mode 100644 build/pkgs/sage_brial/checksums.ini delete mode 100644 build/pkgs/sage_brial/dependencies delete mode 100644 build/pkgs/sage_brial/package-version.txt delete mode 100644 build/pkgs/sage_brial/spkg-install.in delete mode 100644 build/pkgs/sage_brial/spkg-legacy-uninstall.in delete mode 100644 build/pkgs/sage_brial/type diff --git a/build/pkgs/sage_brial/SPKG.rst b/build/pkgs/sage_brial/SPKG.rst deleted file mode 100644 index 3cc3c9181e9..00000000000 --- a/build/pkgs/sage_brial/SPKG.rst +++ /dev/null @@ -1,24 +0,0 @@ -BRiAl -===== - -Description ------------ - -BRiAl is the successor to PolyBoRi. - -The core of PolyBoRi is a C++ library, which provides high-level data -types for Boolean polynomials and monomials, exponent vectors, as well -as for the underlying polynomial rings and subsets of the powerset of -the Boolean variables. This SPKG is a (sage) python wrapper around the -functionality of the C++ library. - -License -------- - -GPL version 2 or later - - -Upstream Contact ----------------- - -https://github.com/BRiAl/BRiAl diff --git a/build/pkgs/sage_brial/checksums.ini b/build/pkgs/sage_brial/checksums.ini deleted file mode 100644 index 2f3607207f7..00000000000 --- a/build/pkgs/sage_brial/checksums.ini +++ /dev/null @@ -1,5 +0,0 @@ -tarball=brial-VERSION.tar.bz2 -sha1=ea69faff56fb7068536723f3fb5b3583b8467831 -md5=d6c6a01d4fc80062550e02d9185bfbff -cksum=318826732 -upstream_url=https://github.com/BRiAl/BRiAl/releases/download/VERSION/brial-VERSION.tar.bz2 diff --git a/build/pkgs/sage_brial/dependencies b/build/pkgs/sage_brial/dependencies deleted file mode 100644 index a3a1eda874c..00000000000 --- a/build/pkgs/sage_brial/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -brial $(PYTHON) | $(PYTHON_TOOLCHAIN) - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sage_brial/package-version.txt b/build/pkgs/sage_brial/package-version.txt deleted file mode 100644 index db6fb4a9113..00000000000 --- a/build/pkgs/sage_brial/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.2.8 diff --git a/build/pkgs/sage_brial/spkg-install.in b/build/pkgs/sage_brial/spkg-install.in deleted file mode 100644 index 314863c1a77..00000000000 --- a/build/pkgs/sage_brial/spkg-install.in +++ /dev/null @@ -1,7 +0,0 @@ -# -# BRiAl consists of a both C++ library and a SageMath-specific python -# module. This spkg installs only the python module; the C++ library -# is installed by the "brial" spkg. -# -export PYTHON=sage-python23 -cd src/sage-brial && sdh_pip_install . diff --git a/build/pkgs/sage_brial/spkg-legacy-uninstall.in b/build/pkgs/sage_brial/spkg-legacy-uninstall.in deleted file mode 100644 index 0e63fe75dec..00000000000 --- a/build/pkgs/sage_brial/spkg-legacy-uninstall.in +++ /dev/null @@ -1,2 +0,0 @@ -echo "Cleaning out old PolyBoRi and BRiAl installations" -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/{polybori,brial} diff --git a/build/pkgs/sage_brial/type b/build/pkgs/sage_brial/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/sage_brial/type +++ /dev/null @@ -1 +0,0 @@ -standard From 1f116f9f6db3c4540bb9de33af043e0b53859c99 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 21 Aug 2020 09:06:31 +0900 Subject: [PATCH 281/379] Fixes for reviewer comments --- src/doc/common/themes/sage/layout.html | 2 +- src/doc/common/themes/sageref/layout.html | 2 +- src/doc/en/reference/euclidean_space/conf.py | 1 - src/doc/en/reference/euclidean_space/index.rst | 11 ----------- src/doc/en/reference/index.rst | 2 +- src/doc/en/website/index.rst | 2 +- src/doc/en/website/templates/index.html | 2 +- src/sage_setup/docbuild/__init__.py | 4 +--- 8 files changed, 6 insertions(+), 20 deletions(-) delete mode 120000 src/doc/en/reference/euclidean_space/conf.py delete mode 100644 src/doc/en/reference/euclidean_space/index.rst diff --git a/src/doc/common/themes/sage/layout.html b/src/doc/common/themes/sage/layout.html index 3f27fece8af..82763e7465a 100644 --- a/src/doc/common/themes/sage/layout.html +++ b/src/doc/common/themes/sage/layout.html @@ -35,7 +35,7 @@ /* The sidebar toggle adapts its height to the bodywrapper height. */ const resizeObserver = new ResizeObserver(entries => { - tog.height(bod.height()); + tog.height(bod.height()); }); resizeObserver.observe(bod[0]); diff --git a/src/doc/common/themes/sageref/layout.html b/src/doc/common/themes/sageref/layout.html index 4d771933f32..d70e25c75c2 100644 --- a/src/doc/common/themes/sageref/layout.html +++ b/src/doc/common/themes/sageref/layout.html @@ -36,7 +36,7 @@ /* The sidebar toggle adapts its height to the bodywrapper height. */ const resizeObserver = new ResizeObserver(entries => { - tog.height(bod.height()); + tog.height(bod.height()); }); resizeObserver.observe(bod[0]); diff --git a/src/doc/en/reference/euclidean_space/conf.py b/src/doc/en/reference/euclidean_space/conf.py deleted file mode 120000 index 2bdf7e68470..00000000000 --- a/src/doc/en/reference/euclidean_space/conf.py +++ /dev/null @@ -1 +0,0 @@ -../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/euclidean_space/index.rst b/src/doc/en/reference/euclidean_space/index.rst deleted file mode 100644 index af4481f32f7..00000000000 --- a/src/doc/en/reference/euclidean_space/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _euclidean-spaces: - -Euclidean Spaces and Vector Calculus -==================================== - -.. toctree:: - :maxdepth: 2 - - sage/manifolds/differentiable/euclidean - - sage/manifolds/operators diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index e532066ea2f..eb11d0fe393 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -96,7 +96,7 @@ Discrete Mathematics Geometry and Topology --------------------- -* :doc:`Euclidean Spaces and Vector Calculus ` +* :doc:`Euclidean Spaces and Vector Calculus ` * :doc:`Combinatorial and Discrete Geometry ` * :doc:`Cell Complexes and their Homology ` * :doc:`Manifolds and Differential Geometry ` diff --git a/src/doc/en/website/index.rst b/src/doc/en/website/index.rst index 9db431db16e..63ff23e4744 100644 --- a/src/doc/en/website/index.rst +++ b/src/doc/en/website/index.rst @@ -3,4 +3,4 @@ Sage Documentation Do not edit this file as its output is overridden by the template in the templates directory. -Do not delete this line as it enables MathJax in the webpage generated from this file: :math:`e^\pi+1=0`. +Do not delete this line as it enables MathJax in the webpage generated from this file: :math:`e^{\pi i}+1=0`. diff --git a/src/doc/en/website/templates/index.html b/src/doc/en/website/templates/index.html index 618fe51971a..c2915b5132c 100644 --- a/src/doc/en/website/templates/index.html +++ b/src/doc/en/website/templates/index.html @@ -255,7 +255,7 @@

        This document collects answers to some questions along the line "How do I construct ... in Sage?" Try to find out how to see the Riemann zeta - function \(\zeta(s)\) along the imaginary line \(s=\frac{1}{2}+it\). + function \(\zeta(s)\) along the line \(s=\frac{1}{2}+it\).

        diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 58e7fad3566..8fab15b28b7 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -38,8 +38,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import absolute_import, print_function - import logging import optparse import os @@ -612,7 +610,7 @@ def _wrapper(self, format, *args, **kwds): # Get rid of todolist and miscellaneous rst markup. rst = rst.replace('.. _reference-manual:\n\n', '') rst = re.sub(r'\\\\', r'\\', rst) - # Replace rst links with html links. There are three forms: + # Replace rst links with html links. There are three forms: # # `blah`__ followed by __ LINK # From 6c881bc53699c7628bc5a5fd8d681ee96c273231 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 21 Aug 2020 10:11:51 +0900 Subject: [PATCH 282/379] Fixes missed in the previous commit --- src/doc/en/reference/euclidean_spaces/conf.py | 1 + src/doc/en/reference/euclidean_spaces/index.rst | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 120000 src/doc/en/reference/euclidean_spaces/conf.py create mode 100644 src/doc/en/reference/euclidean_spaces/index.rst diff --git a/src/doc/en/reference/euclidean_spaces/conf.py b/src/doc/en/reference/euclidean_spaces/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/euclidean_spaces/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/euclidean_spaces/index.rst b/src/doc/en/reference/euclidean_spaces/index.rst new file mode 100644 index 00000000000..af4481f32f7 --- /dev/null +++ b/src/doc/en/reference/euclidean_spaces/index.rst @@ -0,0 +1,11 @@ +.. _euclidean-spaces: + +Euclidean Spaces and Vector Calculus +==================================== + +.. toctree:: + :maxdepth: 2 + + sage/manifolds/differentiable/euclidean + + sage/manifolds/operators From 02a5af16c449d4839658aa5d7ac352dc2227f7d2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 Aug 2020 18:41:09 -0700 Subject: [PATCH 283/379] build/pkgs/sagelib/dependencies: Remove sage_brial --- build/pkgs/sagelib/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies index 8032ab0008c..4db58d59e88 100644 --- a/build/pkgs/sagelib/dependencies +++ b/build/pkgs/sagelib/dependencies @@ -1,4 +1,4 @@ -FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints readline rw sage_brial sage_conf singular symmetrica zn_poly $(PCFILES) +FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints readline rw sage_conf singular symmetrica zn_poly $(PCFILES) ---------- All lines of this file are ignored except the first. From 2e4d97c9169083e8a4f7f6b0fd8660126c689ad8 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Fri, 21 Aug 2020 12:37:29 +0200 Subject: [PATCH 284/379] #30412 : remove old patch --- .../0001-generic_really_is_generic.patch | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 build/pkgs/gf2x/patches/0001-generic_really_is_generic.patch diff --git a/build/pkgs/gf2x/patches/0001-generic_really_is_generic.patch b/build/pkgs/gf2x/patches/0001-generic_really_is_generic.patch deleted file mode 100644 index a2ea7ab3bd4..00000000000 --- a/build/pkgs/gf2x/patches/0001-generic_really_is_generic.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff -urN gf2x-1.2/ChangeLog gf2x-test/ChangeLog ---- gf2x-1.2/ChangeLog 2017-07-03 16:27:03.000000000 +0200 -+++ gf2x-test/ChangeLog 2017-07-04 00:55:55.000000000 +0200 -@@ -1,3 +1,9 @@ -+commit fe124f625f9037c6f9ed22aad50221b87db7101c -+Author: Emmanuel Thomé -+Date: Tue Jul 4 00:55:31 2017 +0200 -+ -+ forcibly disable all the _supports_XXX macros when --disable-hardware-specific-code is used -+ - commit 6a40623ffd93e7b412ffceab80184ef39c3426a7 - Author: Emmanuel Thomé - Date: Thu Jun 29 17:17:11 2017 +0200 -diff -urN gf2x-1.2/configure gf2x-test/configure ---- gf2x-1.2/configure 2017-07-03 16:26:52.000000000 +0200 -+++ gf2x-test/configure 2017-07-04 00:55:47.000000000 +0200 -@@ -17508,6 +17508,13 @@ - - if test "x${enable_hardware_specific_code}" = xno ; then - echo "Not using hardware-specific code due to --disable-hardware-specific-code flag" -+ gf2x_cv_cc_supports_sse2=no -+ gf2x_cv_cc_supports_sse3=no -+ gf2x_cv_cc_supports_ssse3=no -+ gf2x_cv_cc_supports_sse41=no -+ gf2x_cv_cc_supports_pclmul=no -+ gf2x_cv_cc_supports_march_native=no -+ gf2x_cv_cc_supports_mtune_native=no - else - # It is necessary to make all tests. We do encounter in the wild - # binutils (openbsd binutils 2.15, namely) which are buggy with ssse3, -diff -urN gf2x-1.2/configure.ac gf2x-test/configure.ac ---- gf2x-1.2/configure.ac 2017-06-29 10:56:09.000000000 +0200 -+++ gf2x-test/configure.ac 2017-07-04 00:54:59.000000000 +0200 -@@ -114,6 +114,13 @@ - - if test "x${enable_hardware_specific_code}" = xno ; then - echo "Not using hardware-specific code due to --disable-hardware-specific-code flag" -+ gf2x_cv_cc_supports_sse2=no -+ gf2x_cv_cc_supports_sse3=no -+ gf2x_cv_cc_supports_ssse3=no -+ gf2x_cv_cc_supports_sse41=no -+ gf2x_cv_cc_supports_pclmul=no -+ gf2x_cv_cc_supports_march_native=no -+ gf2x_cv_cc_supports_mtune_native=no - else - # It is necessary to make all tests. We do encounter in the wild - # binutils (openbsd binutils 2.15, namely) which are buggy with ssse3, From 3d9b3bb3a380264a7bdd7b91e761ffadea7e4fff Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Fri, 21 Aug 2020 12:38:09 +0200 Subject: [PATCH 285/379] #30412 : update package version --- build/pkgs/gf2x/checksums.ini | 7 ++++--- build/pkgs/gf2x/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/gf2x/checksums.ini b/build/pkgs/gf2x/checksums.ini index a8c1ad9e777..405b2df3da5 100644 --- a/build/pkgs/gf2x/checksums.ini +++ b/build/pkgs/gf2x/checksums.ini @@ -1,4 +1,5 @@ tarball=gf2x-VERSION.tar.gz -sha1=53c8876a1de869332ce126b77596aeb06fe06bff -md5=bff500f8e577c6098af15bc00c7cef37 -cksum=1463710603 +sha1=2a9099d6e3c1d0890e55e93dd4ea635fb1a64a42 +md5=18bccf5efd998424da5e99d4575de0ed +cksum=1705713312 +upstream_url=https://gitlab.inria.fr/gf2x/gf2x/-/archive/gf2x-VERSION/gf2x-gf2x-VERSION.tar.gz diff --git a/build/pkgs/gf2x/package-version.txt b/build/pkgs/gf2x/package-version.txt index d5b3ae357de..f0bb29e7638 100644 --- a/build/pkgs/gf2x/package-version.txt +++ b/build/pkgs/gf2x/package-version.txt @@ -1 +1 @@ -1.2.p0 +1.3.0 From 3c9c8f20e134aaa385b444aacfe3e32bbeb8eb27 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Fri, 21 Aug 2020 12:39:14 +0200 Subject: [PATCH 286/379] #30412 : gf2x does not provide a configure script anymore --- build/pkgs/gf2x/spkg-install.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/gf2x/spkg-install.in b/build/pkgs/gf2x/spkg-install.in index 7ae410af1a8..2abee3cd6d6 100644 --- a/build/pkgs/gf2x/spkg-install.in +++ b/build/pkgs/gf2x/spkg-install.in @@ -7,9 +7,9 @@ cd src # Use newer version of config.guess and config.sub (see Trac #19727) cp "$SAGE_ROOT"/config/config.* config -# As config/acinclude.m4 is patched, we need to touch some files to prevent -# autotools to invoke black magic. -touch aclocal.m4 configure Makefile.in gf2x/gf2x-config.h.in +# Run libtool/autotools +libtoolize +autoreconf -ivf if [ "$SAGE_DEBUG" = "yes" ]; then echo "Building a debug version of gf2x." From eadfe030a20a446057cf24b57a995e61387b356e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Fri, 21 Aug 2020 13:08:19 +0200 Subject: [PATCH 287/379] trac #30377: avoid use of intalled_packages --- src/sage/graphs/generic_graph.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 50e7c5dc17a..df83636d4ae 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -449,11 +449,8 @@ def igraph_feature(): TESTS:: sage: from sage.graphs.generic_graph import igraph_feature - sage: ((igraph_feature().is_present() - ....: and 'igraph' in installed_packages()) - ....: or (not igraph_feature().is_present() - ....: and 'igraph' not in installed_packages())) - True + sage: igraph_feature().is_present() # optional - python_igraph + FeatureTestResult('igraph', True) """ from sage.features import PythonModule return PythonModule("igraph", spkg="python_igraph", url="http://igraph.org") From 12faa1079f424bdfe122754792c6655b818b6a61 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Fri, 21 Aug 2020 09:03:26 -0300 Subject: [PATCH 288/379] Revert "Moved methods to implementation classes" Move back methods to the category level --- .../algebras/lie_conformal_algebras.rst | 1 - .../finitely_freely_generated_lca.py | 3 +- ...initely_generated_lie_conformal_algebra.py | 75 ----- .../graded_lie_conformal_algebra.py | 15 - .../lie_conformal_algebra.py | 20 -- .../lie_conformal_algebra_element.py | 184 +---------- .../lie_conformal_algebra_with_basis.py | 3 +- ...nitely_generated_lie_conformal_algebras.py | 98 ++++++ src/sage/categories/lie_conformal_algebras.py | 285 ++++++++++++++++++ .../lie_conformal_algebras_with_basis.py | 91 ++++++ 10 files changed, 482 insertions(+), 293 deletions(-) delete mode 100644 src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py diff --git a/src/doc/en/reference/algebras/lie_conformal_algebras.rst b/src/doc/en/reference/algebras/lie_conformal_algebras.rst index a20090e29ea..0752d5843b7 100644 --- a/src/doc/en/reference/algebras/lie_conformal_algebras.rst +++ b/src/doc/en/reference/algebras/lie_conformal_algebras.rst @@ -30,7 +30,6 @@ See also .. toctree:: ../sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca - ../sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py index 0b14198d210..b204c4a89b7 100644 --- a/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +++ b/src/sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py @@ -20,9 +20,8 @@ from sage.categories.lie_conformal_algebras import LieConformalAlgebras from .freely_generated_lie_conformal_algebra import \ FreelyGeneratedLieConformalAlgebra -from .finitely_generated_lie_conformal_algebra import FinitelyGeneratedLieConformalAlgebra -class FinitelyFreelyGeneratedLCA(FreelyGeneratedLieConformalAlgebra, FinitelyGeneratedLieConformalAlgebra): +class FinitelyFreelyGeneratedLCA(FreelyGeneratedLieConformalAlgebra): """ Abstract base class for finitely generated Lie conformal algebras. diff --git a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py deleted file mode 100644 index 9084bda77e1..00000000000 --- a/src/sage/algebras/lie_conformal_algebras/finitely_generated_lie_conformal_algebra.py +++ /dev/null @@ -1,75 +0,0 @@ -r""" -Finitely Generated Lie Conformal Algebra - -AUTHORS: - -- Reimundo Heluani (2020-07-09): Initial implementation. -""" - - -#****************************************************************************** -# Copyright (C) 2020 Reimundo Heluani -# -# 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 .lie_conformal_algebra import LieConformalAlgebra - -class FinitelyGeneratedLieConformalAlgebra(LieConformalAlgebra): - """ - Base class for finitely generated (super) Lie conformal algebras. - """ - def gen(self,i): - r""" - The ``i``-th generator of this Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: V.gen(0) - B[alpha[1]] - sage: V.1 - B[alphacheck[1]] - """ - return self.gens()[i] - - def some_elements(self): - """ - Some elements of this Lie conformal algebra. - - This method returns a list with elements containing at - least the generators. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) - sage: V.some_elements() - [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] - """ - S = list(self.gens()) - from sage.misc.misc import some_tuples - for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): - S.append(x.T() + 2*y.T(2)) - return S - - def ngens(self): - r""" - The number of generators of this Lie conformal algebra. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ngens() - 2 - - sage: V = lie_conformal_algebras.Affine(QQ, 'A2') - sage: V.ngens() - 9 - """ - return len(self.gens()) diff --git a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py index 8d9f153fcb5..9f4500d28b4 100644 --- a/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py @@ -130,18 +130,3 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, raise ValueError("weights and (non-central) generator lists "\ "must be of same length") self._weights = weights - - def degree_on_basis(self, m): - r""" - Return the degree of the basis element indexed by ``m`` - in ``self``. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.degree_on_basis(('L',2)) - 4 - """ - if m[0] in self._central_elements: - return 0 - return self._weights[self._index_to_pos[m[0]]] + m[1] diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py index 3a511ed1a7b..0ec6ca765c3 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -349,23 +349,3 @@ def __classcall_private__(cls, R=None, arg0=None, index_set=None, prefix=prefix, names=names, latex_names=latex_names, parity=parity, **kwds) raise NotImplementedError("not implemented") - - - def ideal(self, *gens, **kwds): - r""" - The ideal of this Lie conformal algebra generated by ``gens``. - - .. TODO:: - - Ideals of Lie Conformal Algebras are not implemented yet. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ideal() - Traceback (most recent call last): - ... - NotImplementedError: ideals of Lie Conformal algebras are not implemented yet - """ - raise NotImplementedError("ideals of Lie Conformal algebras are " - "not implemented yet") diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index 3b21946436b..de30de070f6 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -22,184 +22,11 @@ from sage.misc.misc import repr_lincomb from sage.misc.latex import latex from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement -from sage.structure.element import Element -from sage.misc.abstract_method import abstract_method -from sage.structure.element import coerce_binop -class LieConformalAlgebraElement(Element): +class LCAWithGeneratorsElement(IndexedFreeModuleElement): """ - Base class for elements of (super) Lie conformal algebras. - """ - @coerce_binop - def bracket(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - EXAMPLES: - - The brackets of the Virasoro Lie conformal algebra:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.bracket(L) - {0: TL, 1: 2*L, 3: 1/2*C} - sage: L.bracket(L.T()) - {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} - - Now with a current algebra:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: E = V.0; H = V.1; F = V.2; - sage: H.bracket(H) - {1: 2*B['K']} - sage: E.bracket(F) - {0: B[alphacheck[1]], 1: B['K']} - """ - return self._bracket_(rhs) - - @abstract_method - def _bracket_(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES: - - The brackets of the Virasoro Lie conformal Algebra:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L._bracket_(L) - {0: TL, 1: 2*L, 3: 1/2*C} - sage: L._bracket_(L.T()) - {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} - - Now with a current algebra:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: E = V.0; H = V.1; F = V.2; - sage: H._bracket_(H) - {1: 2*B['K']} - sage: E._bracket_(F) - {0: B[alphacheck[1]], 1: B['K']} - """ - - @coerce_binop - def nproduct(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.nproduct(L, 3) - 1/2*C - sage: L.nproduct(L.T(), 0) - 2*T^(2)L - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: E = V.0; H = V.1; F = V.2; - sage: E.nproduct(H, 0) == - 2*E - True - sage: E.nproduct(F, 1) - B['K'] - """ - return self._nproduct_(rhs,n) - - def _nproduct_(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L._nproduct_(L,3) - 1/2*C - sage: L._nproduct_(L.T(),0) - 2*T^(2)L - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: E = V.0; H = V.1; F = V.2; - sage: E._nproduct_(H,0) == - 2*E - True - sage: E._nproduct_(F,1) - B['K'] - """ - if n >= 0: - return self.bracket(rhs).get(n,self.parent().zero()) - else: - raise NotImplementedError("vertex algebras are not implemented") - - @abstract_method - def T(self, n=1): - r""" - The ``n``-th derivative of ``self``. - - INPUT: - - - ``n`` -- integer (default:``1``); how many times - to apply `T` to this element - - OUTPUT: - - `T^n a` where `a` is this element. Notice that we use the - *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.inject_variables() - Defining L, C - sage: L.T() - TL - sage: L.T(3) - 6*T^(3)L - sage: C.T() - 0 - """ - -class LCAWithBasisElement(IndexedFreeModuleElement, LieConformalAlgebraElement): - """ - Element class of a (super) Lie conformal algebra with basis. - """ - def index(self): - """ - The index of this basis element. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3).index() - ('G', 3) - sage: v = V.an_element(); v - L + G + C - sage: v.index() - Traceback (most recent call last): - ... - ValueError: index can only be computed for monomials, got L + G + C - """ - if self.is_zero(): - return None - if not self.is_monomial(): - raise ValueError ("index can only be computed for " - "monomials, got {}".format(self)) - - return next(iter(self.monomial_coefficients())) - -class FreelyGeneratedLCAElement(LCAWithBasisElement): - """ - Element class of a freely generated (super) Lie conformal algebra. + The element class of a Lie conformal algebra with a + preferred set of generators. """ def T(self,n=1): r""" @@ -261,9 +88,10 @@ def is_monomial(self): """ return len(self._monomial_coefficients) == 1 or self.is_zero() -class LCAStructureCoefficientsElement(FreelyGeneratedLCAElement): + +class LCAStructureCoefficientsElement(LCAWithGeneratorsElement): """ - Element class of a (super) Lie conformal algebra given by structure + An element of a Lie conformal algebra given by structure coefficients. """ def _bracket_(self, right): diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py index 07863f06857..e56785601fe 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py @@ -18,9 +18,8 @@ from sage.categories.lie_conformal_algebras import LieConformalAlgebras from sage.combinat.free_module import CombinatorialFreeModule -from .lie_conformal_algebra import LieConformalAlgebra -class LieConformalAlgebraWithBasis(CombinatorialFreeModule, LieConformalAlgebra): +class LieConformalAlgebraWithBasis(CombinatorialFreeModule): """ Abstract base class for a Lie conformal algebra with a preferred basis. diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index f8280e67fa7..90a38209b02 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -29,6 +29,58 @@ class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): sage: LieConformalAlgebras(QQbar).FinitelyGenerated() Category of finitely generated Lie conformal algebras over Algebraic Field """ + class ParentMethods: + def ngens(self): + r""" + The number of generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ngens() + 2 + + sage: V = lie_conformal_algebras.Affine(QQ, 'A2') + sage: V.ngens() + 9 + """ + return len(self.gens()) + + def gen(self,i): + r""" + The ``i``-th generator of this Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: V.gen(0) + B[alpha[1]] + sage: V.1 + B[alphacheck[1]] + """ + return self.gens()[i] + + def some_elements(self): + """ + Some elements of this Lie conformal algebra. + + This method returns a list with elements containing at + least the generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.some_elements() + [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + class Super(SuperModulesCategory): """ The category of super finitely generated Lie conformal algebras. @@ -38,6 +90,52 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).FinitelyGenerated().Super() Category of super finitely generated Lie conformal algebras over Algebraic Real Field """ + class ParentMethods: + def ngens(self): + r""" + The number of generators of this super Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.ngens() + 3 + """ + return len(self.gens()) + + def gen(self,i): + r""" + The ``i``-th generator of this super Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.gens() + (L, G, C) + sage: V.gen(0) + L + """ + return self.gens()[i] + + def some_elements(self): + """ + Some elements of this super Lie conformal algebra. + + This method returns a list with elements containing at + least the generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.some_elements() + [L, G, C, TG + 4*T^(2)L, TG + 4*T^(2)G, TL + 4*T^(2)L] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + class Graded(GradedModulesCategory): """ The category of H-graded super finitely generated Lie conformal algebras. diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index bedd04145aa..ec4d4eb24d7 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -131,6 +131,7 @@ from .category_types import Category_over_base_ring from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method +from sage.structure.element import coerce_binop from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory from sage.categories.commutative_rings import CommutativeRings @@ -267,6 +268,25 @@ def _repr_object_names(self): class ParentMethods: + def ideal(self, *gens, **kwds): + r""" + The ideal of this Lie conformal algebra generated by ``gens``. + + .. TODO:: + + Ideals of Lie Conformal Algebras are not implemented yet. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ideal() + Traceback (most recent call last): + ... + NotImplementedError: ideals of Lie Conformal algebras are not implemented yet + """ + raise NotImplementedError("ideals of Lie Conformal algebras are " + "not implemented yet") + def is_super(self): """ Wether this Lie conformal algebra is a super Lie @@ -399,6 +419,142 @@ def _test_jacobi(self, **options): tester.assertDictEqual(jacobiator, {}) class ElementMethods: + @coerce_binop + def bracket(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal algebra:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L.bracket(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H.bracket(H) + {1: 2*B['K']} + sage: E.bracket(F) + {0: B[alphacheck[1]], 1: B['K']} + """ + return self._bracket_(rhs) + + @abstract_method + def _bracket_(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal Algebra:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L._bracket_(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L._bracket_(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H._bracket_(H) + {1: 2*B['K']} + sage: E._bracket_(F) + {0: B[alphacheck[1]], 1: B['K']} + """ + + @coerce_binop + def nproduct(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.nproduct(L, 3) + 1/2*C + sage: L.nproduct(L.T(), 0) + 2*T^(2)L + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E.nproduct(H, 0) == - 2*E + True + sage: E.nproduct(F, 1) + B['K'] + """ + return self._nproduct_(rhs,n) + + def _nproduct_(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L._nproduct_(L,3) + 1/2*C + sage: L._nproduct_(L.T(),0) + 2*T^(2)L + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E._nproduct_(H,0) == - 2*E + True + sage: E._nproduct_(F,1) + B['K'] + """ + if n >= 0: + return self.bracket(rhs).get(n,self.parent().zero()) + else: + raise NotImplementedError("vertex algebras are not implemented") + + @abstract_method + def T(self, n=1): + r""" + The ``n``-th derivative of ``self``. + + INPUT: + + - ``n`` -- integer (default:``1``); how many times + to apply `T` to this element + + OUTPUT: + + `T^n a` where `a` is this element. Notice that we use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.inject_variables() + Defining L, C + sage: L.T() + TL + sage: L.T(3) + 6*T^(3)L + sage: C.T() + 0 + """ def is_even_odd(self): """ @@ -471,6 +627,27 @@ def example(self): class ParentMethods: + def ideal(self, *gens, **kwds): + r""" + The ideal of this super Lie conformal algebra generated + by ``gens``. + + .. TODO:: + + Ideals of super Lie Conformal Algebras are not + implemented yet. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.ideal() + Traceback (most recent call last): + ... + NotImplementedError: ideals of super Lie Conformal algebras are not implemented yet + """ + raise NotImplementedError("ideals of super Lie Conformal algebras " + "are not implemented yet") + def is_super(self): """ This method returns ``True``. @@ -618,6 +795,100 @@ def _test_jacobi(self, **options): tester.assertDictEqual(jacobiator, {}) class ElementMethods: + @coerce_binop + def bracket(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + EXAMPLES: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.bracket(G.T()) + {0: 2*TL, 1: 2*L, 3: 2*C} + """ + return self._bracket_(rhs) + + @abstract_method + def _bracket_(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G._bracket_(G.T()) + {0: 2*TL, 1: 2*L, 3: 2*C} + """ + + @coerce_binop + def nproduct(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.nproduct(G.T(), 1) + 2*L + """ + return self._nproduct_(rhs, n) + + def _nproduct_(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.nproduct(G.T(), 1) + 2*L + """ + if n >= 0: + return self.bracket(rhs).get(n,self.parent().zero()) + else: + raise NotImplementedError("super vertex algebras are not implemented") + + @abstract_method + def T(self, n=1): + r""" + The ``n``-th derivative of ``self``. + + INPUT: + + - ``n`` -- integer (default:``1``); how many times + to apply `T` to this element + + OUTPUT: + + `T^n a` where `a` is this element. Notice that we use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3) + 6*T^(3)G + """ @abstract_method def is_even_odd(self): @@ -692,6 +963,20 @@ def Super(self, base_ring=None): assert base_ring is None or base_ring is self.base_ring() return SuperModulesCategory.category_of( self.base_category()).Graded() + class ElementMethods: + @abstract_method + def degree(self): + """ + The degree of this element. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.degree() + 2 + sage: L.T(3).degree() + 5 + """ WithBasis = LazyImport('sage.categories.lie_conformal_algebras_with_basis', 'LieConformalAlgebrasWithBasis') diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index 5894227af41..98d0907f8b4 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -29,6 +29,34 @@ class LieConformalAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): sage: LieConformalAlgebras(QQbar).WithBasis() Category of Lie conformal algebras with basis over Algebraic Field """ + class ElementMethods: + + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for " + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + class Super(SuperModulesCategory): """ The category of super Lie conformal algebras with basis. @@ -58,6 +86,34 @@ def _even_odd_on_basis(self, m): """ return self._parity[self.monomial((m[0],0))] + class ElementMethods: + + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for " + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + class Graded(GradedModulesCategory): """ The category of H-graded super Lie conformal algebras with basis. @@ -149,6 +205,23 @@ def _repr_object_names(self): return "H-graded {}".format(self.base_category().\ _repr_object_names()) + class ParentMethods: + + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.degree_on_basis(('L',2)) + 4 + """ + if m[0] in self._central_elements: + return 0 + return self._weights[self._index_to_pos[m[0]]] + m[1] + class Graded(GradedModulesCategory): """ The category of H-graded finitely generated Lie conformal @@ -170,3 +243,21 @@ def _repr_object_names(self): """ return "H-graded {}".format(self.base_category().\ _repr_object_names()) + + class ParentMethods: + + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.degree_on_basis(('L',2)) + 4 + """ + if m[0] in self._central_elements: + return 0 + return self._weights[self._index_to_pos[m[0]]] + m[1] + From 081643ceb77e33e83a340dfefeb61ae7e64f6bcd Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Thu, 20 Aug 2020 21:35:20 +0200 Subject: [PATCH 289/379] 29243: fix details --- src/sage/matrix/matrix2.pyx | 15 +++++-- src/sage/matrix/matrix_double_dense.pyx | 51 +++++++++++++++++++---- src/sage/matrix/matrix_symbolic_dense.pyx | 18 ++++++++ 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index e2d0dca285e..aa4766bf57d 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -6038,13 +6038,16 @@ cdef class Matrix(Matrix1): self.cache('eigenvalues', eigenvalues) return eigenvalues - def eigenvectors_left(self, other=None, extend=True): + def eigenvectors_left(self, other=None, *, extend=True): r""" Compute the left eigenvectors of a matrix. INPUT: - - ``other`` -- not supported + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved (currently supported only if the base ring of ``self`` is + ``RDF`` or ``CDF``) - ``extend`` -- boolean (default: ``True``) @@ -6117,6 +6120,7 @@ cdef class Matrix(Matrix1): deprecation(29243, '"extend" should be used as keyword argument') extend = other + other = None else: raise NotImplementedError('generalized eigenvector ' 'decomposition is implemented ' @@ -6162,13 +6166,16 @@ cdef class Matrix(Matrix1): left_eigenvectors = eigenvectors_left - def eigenvectors_right(self, other=None, extend=True): + def eigenvectors_right(self, other=None, *, extend=True): r""" Compute the right eigenvectors of a matrix. INPUT: - - ``other`` -- not supported + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved (currently supported only if the base ring of ``self`` is + ``RDF`` or ``CDF``) - ``extend`` -- boolean (default: ``True``) diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index d2caea3bbcd..1333d64997e 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -1204,7 +1204,7 @@ cdef class Matrix_double_dense(Matrix_dense): self.cache('PLU_factors', PLU) return PLU - def eigenvalues(self, other=None, algorithm='default', tol=None, + def eigenvalues(self, other=None, algorithm='default', tol=None, *, homogeneous=False): r""" Return a list of ordinary or generalized eigenvalues. @@ -1380,6 +1380,10 @@ cdef class Matrix_double_dense(Matrix_dense): Traceback (most recent call last): ... ValueError: matrix must be square, not 2 x 3 + sage: matrix.identity(CDF, 2).eigenvalues(A) + Traceback (most recent call last): + ... + ValueError: other matrix must be square, not 2 x 3 sage: A = matrix(CDF, 2, [1, 2, 3, 4*I]) sage: A.eigenvalues(algorithm='symmetric') @@ -1411,21 +1415,52 @@ cdef class Matrix_double_dense(Matrix_dense): sage: B = matrix(CDF, [[2, 1+I], [1-I, 3]]) sage: A.eigenvalues(B, algorithm='hermitian', homogeneous=True) # tol 1e-14 [(0.25, 1.0), (1.0, 1.0)] + + Test the deprecation:: + + sage: A = graphs.PetersenGraph().adjacency_matrix().change_ring(RDF) + sage: ev = A.eigenvalues('symmetric', 1e-13) + doctest:...: DeprecationWarning: "extend" and "tol" should be used + as keyword argument only + See https://trac.sagemath.org/29243 for details. + sage: ev # tol 1e-13 + [(-2.0, 4), (1.0, 5), (3.0, 1)] + sage: A.eigenvalues('symmetric', 1e-13, tol=1e-12) + Traceback (most recent call last): + ... + TypeError: eigenvalues() got multiple values for keyword argument 'tol' + sage: A.eigenvalues('symmetric', algorithm='hermitian') + Traceback (most recent call last): + ... + TypeError: eigenvalues() got multiple values for keyword argument 'algorithm' """ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF if isinstance(other, str): # for backward compatibilty, allow algorithm to be passed as first - # positional argument + # positional argument and tol as second positional argument + from sage.misc.superseded import deprecation + deprecation(29243, '"extend" and "tol" should be used as ' + 'keyword argument only') + if algorithm != 'default': + if isinstance(algorithm, str): + raise TypeError("eigenvalues() got multiple values for " + "keyword argument 'algorithm'") + if tol is not None: + raise TypeError("eigenvalues() got multiple values for " + "keyword argument 'tol'") + tol = algorithm algorithm = other other = None if not algorithm in ['default', 'symmetric', 'hermitian']: msg = "algorithm must be 'default', 'symmetric', or 'hermitian', not {0}" raise ValueError(msg.format(algorithm)) - if not self.is_square() or other is not None and not other.is_square(): - msg = 'matrix must be square, not {0} x {1}' - m = self if not self.is_square() else other - raise ValueError(msg.format(m.nrows(), m.ncols())) + if not self.is_square(): + raise ValueError('matrix must be square, not %s x %s' + % (self.nrows(), self.ncols())) + if other is not None and not other.is_square(): + raise ValueError('other matrix must be square, not %s x %s' + % (other.nrows(), other.ncols())) if algorithm == 'symmetric': if self.base_ring() != RDF: try: @@ -1503,7 +1538,7 @@ cdef class Matrix_double_dense(Matrix_dense): ev_group[location][2] = ev_group[location][0]/ev_group[location][1] return [(return_class(avg), m) for _, m, avg in ev_group] - def left_eigenvectors(self, other=None, homogeneous=False): + def left_eigenvectors(self, other=None, *, homogeneous=False): r""" Compute the ordinary or generalized left eigenvectors of a matrix of double precision real or complex numbers (i.e. ``RDF`` or ``CDF``). @@ -1661,7 +1696,7 @@ cdef class Matrix_double_dense(Matrix_dense): eigenvectors_left = left_eigenvectors - def right_eigenvectors(self, other=None, homogeneous=False): + def right_eigenvectors(self, other=None, *, homogeneous=False): r""" Compute the ordinary or generalized right eigenvectors of a matrix of double precision real or complex numbers (i.e. ``RDF`` or ``CDF``). diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index b00a41a6a7b..49573c5109a 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -187,6 +187,15 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): r""" Compute the left eigenvectors of a matrix. + INPUT: + + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved (currently supported only if the base ring of ``self`` is + ``RDF`` or ``CDF``) + + OUTPUT: + For each distinct eigenvalue, returns a list of the form (e,V,n) where e is the eigenvalue, V is a list of eigenvectors forming a basis for the corresponding left eigenspace, and n is the @@ -286,6 +295,15 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): r""" Compute the right eigenvectors of a matrix. + INPUT: + + - ``other`` -- a square matrix `B` (default: ``None``) in a generalized + eigenvalue problem; if ``None``, an ordinary eigenvalue problem is + solved (currently supported only if the base ring of ``self`` is + ``RDF`` or ``CDF``) + + OUTPUT: + For each distinct eigenvalue, returns a list of the form (e,V,n) where e is the eigenvalue, V is a list of eigenvectors forming a basis for the corresponding right eigenspace, and n is the From 04a92efa3f51f469287d1c4b813530d2d9f4f708 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 Aug 2020 16:26:55 +0200 Subject: [PATCH 290/379] fix missing conversion to RDF --- .../geometry/polyhedron/cdd_file_format.py | 22 ++++++++++++++----- src/sage/geometry/polyhedron/misc.py | 8 ++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/polyhedron/cdd_file_format.py b/src/sage/geometry/polyhedron/cdd_file_format.py index 462b642a040..87fee4dd4e6 100644 --- a/src/sage/geometry/polyhedron/cdd_file_format.py +++ b/src/sage/geometry/polyhedron/cdd_file_format.py @@ -62,6 +62,12 @@ def cdd_Vrepresentation(cdd_type, vertices, rays, lines, file_output=None): vertices = [[0]*ambient_dim] num += 1 + if cdd_type is 'real': + from sage.rings.all import RDF + base_ring = RDF + else: + base_ring = None + s = 'V-representation\n' if lines is not None: n = len(lines) @@ -71,13 +77,13 @@ def cdd_Vrepresentation(cdd_type, vertices, rays, lines, file_output=None): s += ' ' + repr(num) + ' ' + repr(ambient_dim+1) + ' ' + cdd_type + '\n' if lines is not None: for l in lines: - s += ' 0 ' + _to_space_separated_string(l) + '\n' + s += ' 0 ' + _to_space_separated_string(l, base_ring) + '\n' if rays is not None: for r in rays: - s += ' 0 ' + _to_space_separated_string(r) + '\n' + s += ' 0 ' + _to_space_separated_string(r, base_ring) + '\n' if vertices is not None: for v in vertices: - s += ' 1 ' + _to_space_separated_string(v) + '\n' + s += ' 1 ' + _to_space_separated_string(v, base_ring) + '\n' s += 'end\n' if file_output is not None: @@ -116,6 +122,12 @@ def cdd_Hrepresentation(cdd_type, ieqs, eqns, file_output=None): num, ambient_dim = _common_length_of(ieqs, eqns) ambient_dim -= 1 + if cdd_type is 'real': + from sage.rings.all import RDF + base_ring = RDF + else: + base_ring = None + s = 'H-representation\n' if eqns is not None: assert len(eqns)>0 @@ -126,10 +138,10 @@ def cdd_Hrepresentation(cdd_type, ieqs, eqns, file_output=None): s += ' ' + repr(num) + ' ' + repr(ambient_dim+1) + ' ' + cdd_type + '\n' if eqns is not None: for e in eqns: - s += ' ' + _to_space_separated_string(e) + '\n' + s += ' ' + _to_space_separated_string(e, base_ring) + '\n' if ieqs is not None: for i in ieqs: - s += ' ' + _to_space_separated_string(i) + '\n' + s += ' ' + _to_space_separated_string(i, base_ring) + '\n' s += 'end\n' if file_output is not None: diff --git a/src/sage/geometry/polyhedron/misc.py b/src/sage/geometry/polyhedron/misc.py index 7f85c3ca7e8..ff41b7a2ca2 100644 --- a/src/sage/geometry/polyhedron/misc.py +++ b/src/sage/geometry/polyhedron/misc.py @@ -11,7 +11,7 @@ # ********************************************************************** -def _to_space_separated_string(l): +def _to_space_separated_string(l, base_ring=None): """ Convert a container to a space-separated string. @@ -19,6 +19,8 @@ def _to_space_separated_string(l): - ``l`` -- anything iterable. + - ``base_ring`` -- ring (default: ``None``); convert this ring, if given + OUTPUT: String. @@ -28,7 +30,11 @@ def _to_space_separated_string(l): sage: import sage.geometry.polyhedron.misc as P sage: P._to_space_separated_string([2,3]) '2 3' + sage: P._to_space_separated_string([2, 1/5], RDF) + '2.0 0.2' """ + if base_ring: + return ' '.join(repr(base_ring(x)) for x in l) return ' '.join(repr(x) for x in l) From 3cf080c666e46fd554410dfbec606f8666af0e3a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 Aug 2020 16:27:48 +0200 Subject: [PATCH 291/379] check lawrence construction with new vertex in RDF --- src/sage/geometry/polyhedron/base.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index eb9c7b713b8..99e385f2fc6 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6091,15 +6091,23 @@ def _test_lawrence(self, tester=None, **options): tester.assertEqual(self.n_vertices() + 2, Q.n_vertices()) tester.assertEqual(self.backend(), Q.backend()) # Any backend should handle the fraction field. - ''' - # Known bug. - # See :trac:`30328`. - R = self.lawrence_extension(2.0*v - self.center()) - tester.assertEqual(self.dim() + 1, R.dim()) - tester.assertEqual(self.n_vertices() + 2, R.n_vertices()) - - tester.assertTrue(Q.is_combinatorially_isomorphic(R)) - ''' + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("error") + try: + # Implicitely checks :trac:`30328`. + R = self.lawrence_extension(2.0*v - self.center()) + tester.assertEqual(self.dim() + 1, R.dim()) + tester.assertEqual(self.n_vertices() + 2, R.n_vertices()) + + tester.assertTrue(Q.is_combinatorially_isomorphic(R)) + except UserWarning: + # Data is numerically complicated. + pass + except ValueError as err: + if not "Numerical inconsistency" in err.args[0]: + raise err if self.n_vertices() >= 12 or (self.base_ring() not in (ZZ, QQ) and self.backend() == 'field'): # Avoid very long tests. From 0be9ed32eb667a4e3bf8b7d460dfa5c7cfaad760 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 Aug 2020 16:56:23 +0200 Subject: [PATCH 292/379] python 3.8 compatible --- src/sage/geometry/polyhedron/cdd_file_format.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/cdd_file_format.py b/src/sage/geometry/polyhedron/cdd_file_format.py index 87fee4dd4e6..d4b12a85fe2 100644 --- a/src/sage/geometry/polyhedron/cdd_file_format.py +++ b/src/sage/geometry/polyhedron/cdd_file_format.py @@ -62,7 +62,7 @@ def cdd_Vrepresentation(cdd_type, vertices, rays, lines, file_output=None): vertices = [[0]*ambient_dim] num += 1 - if cdd_type is 'real': + if cdd_type == 'real': from sage.rings.all import RDF base_ring = RDF else: @@ -122,7 +122,7 @@ def cdd_Hrepresentation(cdd_type, ieqs, eqns, file_output=None): num, ambient_dim = _common_length_of(ieqs, eqns) ambient_dim -= 1 - if cdd_type is 'real': + if cdd_type == 'real': from sage.rings.all import RDF base_ring = RDF else: From ff8ac0980645c6cc2d913dee55b03575bfbe48b7 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 21 Aug 2020 20:57:36 +0200 Subject: [PATCH 293/379] set cdvision to true by default with cython magic --- src/sage/misc/cython.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 0600603e62d..a69b7927bf2 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -193,6 +193,12 @@ def cython(filename, verbose=0, compile_message=False, Traceback (most recent call last): ... RuntimeError: ... + + As of :trac:`29139` the default is ``cdivision=True``:: + + sage: cython(''' + ....: cdef size_t foo = 3/2 + ....: ''') """ if not filename.endswith('pyx'): print("Warning: file (={}) should have extension .pyx".format(filename), file=sys.stderr) @@ -314,7 +320,7 @@ def cython(filename, verbose=0, compile_message=False, libraries=standard_libs, library_dirs=standard_libdirs) - directives = dict(language_level=sys.version_info[0]) + directives = dict(language_level=sys.version_info[0], cdivision=True) try: # Change directories to target_dir so that Cython produces the correct From 6c92608175d3b675cc138cd6af1c1bcd0168bbdf Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Fri, 21 Aug 2020 16:16:09 -0300 Subject: [PATCH 294/379] Implemented LambdaBracketAlgebras These are mostly reviewer's changes. Implemented an abstract category LambdaBracketAlgebras which both LieConformalAlgebras and SuperLieConformalAlgebras inherit. --- src/doc/en/reference/categories/index.rst | 2 + src/sage/categories/category_with_axiom.py | 4 +- ...nitely_generated_lie_conformal_algebras.py | 33 +- .../graded_lie_conformal_algebras.py | 71 ++ .../categories/lambda_bracket_algebras.py | 264 +++++++ src/sage/categories/lie_conformal_algebras.py | 684 +----------------- .../lie_conformal_algebras_with_basis.py | 62 +- .../super_lie_conformal_algebras.py | 188 +++++ 8 files changed, 580 insertions(+), 728 deletions(-) create mode 100644 src/sage/categories/graded_lie_conformal_algebras.py create mode 100644 src/sage/categories/lambda_bracket_algebras.py create mode 100644 src/sage/categories/super_lie_conformal_algebras.py diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index 7d91d976a68..c9f4f270628 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -112,6 +112,7 @@ Individual Categories sage/categories/graded_hopf_algebras_with_basis sage/categories/graded_lie_algebras sage/categories/graded_lie_algebras_with_basis + sage/categories/graded_lie_conformal_algebras sage/categories/graded_modules sage/categories/graded_modules_with_basis sage/categories/graphs @@ -177,6 +178,7 @@ Individual Categories sage/categories/super_algebras sage/categories/super_algebras_with_basis sage/categories/super_hopf_algebras_with_basis + sage/categories/super_lie_conformal_algebras sage/categories/super_modules sage/categories/super_modules_with_basis sage/categories/supercommutative_algebras diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 6a609047faa..3009315f121 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1681,7 +1681,7 @@ class ``Sets.Finite``), or in a separate file (typically in a class "Complete", "Nilpotent", "FiniteDimensional", "Connected", - "FinitelyGeneratedAsLieConformalAlgebra", + "FinitelyGeneratedAsLambdaBracketAlgebra", "WithBasis", "Irreducible", "Supercommutative", "Supercocommutative", @@ -2286,7 +2286,7 @@ def _repr_object_names_static(category, axioms): elif axiom == "FinitelyGeneratedAsMagma" and \ not base_category.is_subcategory(AdditiveMagmas()): result = "finitely generated " + result - elif axiom == "FinitelyGeneratedAsLieConformalAlgebra": + elif axiom == "FinitelyGeneratedAsLambdaBracketAlgebra": result = "finitely generated " + result else: result = uncamelcase(axiom) + " " + result diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 90a38209b02..116c732d2e6 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -19,16 +19,18 @@ from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory +from sage.categories.lie_conformal_algebras import LieConformalAlgebras -class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): +class FinitelyGeneratedLieConformalAlgebras(CategoryWithAxiom_over_base_ring): """ The category of finitely generated Lie conformal algebras. EXAMPLES:: sage: LieConformalAlgebras(QQbar).FinitelyGenerated() - Category of finitely generated Lie conformal algebras over Algebraic Field + Category of finitely generated lie conformal algebras over Algebraic Field """ + _base_category_class_and_axiom = (LieConformalAlgebras, "FinitelyGeneratedAsLambdaBracketAlgebra") class ParentMethods: def ngens(self): r""" @@ -88,7 +90,7 @@ class Super(SuperModulesCategory): EXAMPLES:: sage: LieConformalAlgebras(AA).FinitelyGenerated().Super() - Category of super finitely generated Lie conformal algebras over Algebraic Real Field + Category of super finitely generated lie conformal algebras over Algebraic Real Field """ class ParentMethods: def ngens(self): @@ -143,19 +145,19 @@ class Graded(GradedModulesCategory): EXAMPLES:: sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Super().Graded() - Category of H-graded super finitely generated Lie conformal algebras over Algebraic Field + Category of H-graded super finitely generated lie conformal algebras over Algebraic Field """ def _repr_object_names(self): """ - The names of the objects of this category + The names of the objects of ``self``. EXAMPLES:: - sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Super().Graded() - Category of H-graded super finitely generated Lie conformal algebras over Algebraic Field + sage: C = LieConformalAlgebras(QQbar).FinitelyGenerated() + sage: C.Super().Graded() + Category of H-graded super finitely generated lie conformal algebras over Algebraic Field """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) + return "H-graded {}".format(self.base_category()._repr_object_names()) class Graded(GradedModulesCategory): """ @@ -164,16 +166,17 @@ class Graded(GradedModulesCategory): EXAMPLES:: sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() - Category of H-graded finitely generated Lie conformal algebras over Algebraic Field + Category of H-graded finitely generated lie conformal algebras over Algebraic Field """ def _repr_object_names(self): """ - The names of the objects of this category + The names of the objects of ``self``. EXAMPLES:: - sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() - Category of H-graded finitely generated Lie conformal algebras over Algebraic Field + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) + return "H-graded {}".format(self.base_category()._repr_object_names()) + + diff --git a/src/sage/categories/graded_lie_conformal_algebras.py b/src/sage/categories/graded_lie_conformal_algebras.py new file mode 100644 index 00000000000..885caac3870 --- /dev/null +++ b/src/sage/categories/graded_lie_conformal_algebras.py @@ -0,0 +1,71 @@ +""" +Graded Lie Conformal Algebras + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.graded_modules import GradedModulesCategory +from sage.misc.cachefunc import cached_method + +class GradedLieConformalAlgebrasCategory(GradedModulesCategory): + @cached_method + def Super(self, base_ring=None): + r""" + Return the super-analogue category of ``self``. + + INPUT: + + - ``base_ring`` -- this is ignored + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar) + sage: C.Graded().Super() is C.Super().Graded() + True + sage: Cp = C.WithBasis() + sage: Cp.Graded().Super() is Cp.Super().Graded() + True + """ + return self.base_category().Super(base_ring).Graded() + + def _repr_object_names(self): + """ + The names of the objects of ``self``. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field + """ + return "H-graded {}".format(self.base_category()._repr_object_names()) + +class GradedLieConformalAlgebras(GradedLieConformalAlgebrasCategory): + """ + The category of graded Lie conformal algebras. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar).Graded(); C + Category of H-graded Lie conformal algebras over Algebraic Field + + sage: CS = LieConformalAlgebras(QQ).Graded().Super(); CS + Category of H-graded super Lie conformal algebras over Rational Field + sage: CS is LieConformalAlgebras(QQ).Super().Graded() + True + """ + diff --git a/src/sage/categories/lambda_bracket_algebras.py b/src/sage/categories/lambda_bracket_algebras.py new file mode 100644 index 00000000000..ef15e130830 --- /dev/null +++ b/src/sage/categories/lambda_bracket_algebras.py @@ -0,0 +1,264 @@ +r""" +Lambda Bracket Algebras + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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 .category_types import Category_over_base_ring +from sage.misc.abstract_method import abstract_method +from sage.categories.modules import Modules +from sage.structure.element import coerce_binop +from sage.misc.cachefunc import cached_method +from sage.categories.commutative_rings import CommutativeRings +_CommutativeRings = CommutativeRings() + + +class LambdaBracketAlgebras(Category_over_base_ring): + r""" + The category of Lambda bracket algebras. + + This is an abstract base category for Lie conformal algebras and + super Lie conformal algebras. + + """ + @staticmethod + def __classcall_private__(cls, R, check=True): + r""" + INPUT: + + - `R` -- a commutative ring + - ``check`` -- a boolean (default: ``True``); whether to check + that `R` is a commutative ring + + EXAMPLES:: + + sage: LieConformalAlgebras(QuaternionAlgebra(2)) + Traceback (most recent call last): + ValueError: base must be a commutative ring got Quaternion Algebra (-1, -1) with base ring Rational Field + sage: LieConformalAlgebras(ZZ) + Category of Lie conformal algebras over Integer Ring + """ + if check: + if not (R in _CommutativeRings): + raise ValueError("base must be a commutative ring got {}".format(R)) + return super(LambdaBracketAlgebras, cls).__classcall__(cls, R) + + @cached_method + def super_categories(self): + """ + The list of super categories of this category. + + EXAMPLES:: + + sage: from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras + sage: LambdaBracketAlgebras(QQ).super_categories() + [Category of vector spaces over Rational Field] + """ + return [Modules(self.base_ring())] + + def _repr_object_names(self): + """ + The name of the objects of this category. + + EXAMPLES:: + + sage: from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras + sage: LambdaBracketAlgebras(QQ) + Category of Lambda bracket algebras over Rational Field + """ + return "Lambda bracket algebras over {}".format(self.base_ring()) + + class SubcategoryMethods: + + def FinitelyGeneratedAsLambdaBracketAlgebra(self): + """ + The category of finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated() + Category of finitely generated lie conformal algebras over Rational Field + """ + return self._with_axiom("FinitelyGeneratedAsLambdaBracketAlgebra") + + def FinitelyGenerated(self): + """ + The category of finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).FinitelyGenerated() + Category of finitely generated lie conformal algebras over Rational Field + """ + return self._with_axiom("FinitelyGeneratedAsLambdaBracketAlgebra") + + class ParentMethods: + + def ideal(self, *gens, **kwds): + r""" + The ideal of this Lie conformal algebra generated by ``gens``. + + .. TODO:: + + Ideals of Lie Conformal Algebras are not implemented yet. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ideal() + Traceback (most recent call last): + ... + NotImplementedError: ideals of Lie Conformal algebras are not implemented yet + """ + raise NotImplementedError("ideals of Lie Conformal algebras are " + "not implemented yet") + class ElementMethods: + + @coerce_binop + def bracket(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal algebra:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.bracket(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L.bracket(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H.bracket(H) + {1: 2*B['K']} + sage: E.bracket(F) + {0: B[alphacheck[1]], 1: B['K']} + """ + return self._bracket_(rhs) + + @abstract_method + def _bracket_(self, rhs): + r""" + The `\lambda`-bracket of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES: + + The brackets of the Virasoro Lie conformal Algebra:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L._bracket_(L) + {0: TL, 1: 2*L, 3: 1/2*C} + sage: L._bracket_(L.T()) + {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} + + Now with a current algebra:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: E = V.0; H = V.1; F = V.2; + sage: H._bracket_(H) + {1: 2*B['K']} + sage: E._bracket_(F) + {0: B[alphacheck[1]], 1: B['K']} + """ + + @coerce_binop + def nproduct(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L.nproduct(L, 3) + 1/2*C + sage: L.nproduct(L.T(), 0) + 2*T^(2)L + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E.nproduct(H, 0) == - 2*E + True + sage: E.nproduct(F, 1) + B['K'] + """ + return self._nproduct_(rhs,n) + + def _nproduct_(self, rhs, n): + r""" + The ``n``-th product of these two elements. + + .. NOTE:: + + It is guaranteed that both are elements of the same + parent. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 + sage: L._nproduct_(L,3) + 1/2*C + sage: L._nproduct_(L.T(),0) + 2*T^(2)L + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: E = V.0; H = V.1; F = V.2; + sage: E._nproduct_(H,0) == - 2*E + True + sage: E._nproduct_(F,1) + B['K'] + """ + if n >= 0: + return self.bracket(rhs).get(n,self.parent().zero()) + else: + raise NotImplementedError("vertex algebras are not implemented") + + @abstract_method + def T(self, n=1): + r""" + The ``n``-th derivative of ``self``. + + INPUT: + + - ``n`` -- integer (default:``1``); how many times + to apply `T` to this element + + OUTPUT: + + `T^n a` where `a` is this element. Notice that we use the + *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.inject_variables() + Defining L, C + sage: L.T() + TL + sage: L.T(3) + 6*T^(3)L + sage: C.T() + 0 + """ diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index ec4d4eb24d7..c889444cb56 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -2,13 +2,11 @@ Lie Conformal Algebras Let `R` be a commutative ring, a *super Lie conformal algebra* -[Kac1997]_ over `R` -(also known as a *vertex Lie algebra*) is an `R[T]` super module `L` -together with a `\mathbb{Z}/2\mathbb{Z}`-graded `R`-bilinear -operation (called the `\lambda`-bracket) -`L\otimes L \rightarrow L[\lambda]` -(polynomials in `\lambda` with -coefficients in `L`), `a \otimes b \mapsto [a_\lambda b]` satisfying +[Kac1997]_ over `R` (also known as a *vertex Lie algebra*) is an `R[T]` +super module `L` together with a `\mathbb{Z}/2\mathbb{Z}`-graded `R`-bilinear +operation (called the `\lambda`-bracket) `L\otimes L \rightarrow L[\lambda]` +(polynomials in `\lambda` with coefficients in `L`), +`a \otimes b \mapsto [a_\lambda b]` satisfying 1. Sesquilinearity: @@ -127,18 +125,11 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.modules import Modules from .category_types import Category_over_base_ring -from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method -from sage.structure.element import coerce_binop -from sage.categories.graded_modules import GradedModulesCategory -from sage.categories.super_modules import SuperModulesCategory -from sage.categories.commutative_rings import CommutativeRings +from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras from sage.misc.lazy_import import LazyImport -_CommutativeRings = CommutativeRings() - class LieConformalAlgebras(Category_over_base_ring): r""" The category of Lie conformal algebras. @@ -182,28 +173,6 @@ class LieConformalAlgebras(Category_over_base_ring): ValueError: base must be a commutative ring got Quaternion Algebra (-1, -1) with base ring Rational Field """ - @staticmethod - def __classcall_private__(cls, R, check=True): - r""" - INPUT: - - - `R` -- a commutative ring - - ``check`` -- a boolean (default: ``True``); whether to check - that `R` is a commutative ring - - EXAMPLES:: - - sage: LieConformalAlgebras(QuaternionAlgebra(2)) - Traceback (most recent call last): - ValueError: base must be a commutative ring got Quaternion Algebra (-1, -1) with base ring Rational Field - sage: LieConformalAlgebras(ZZ) - Category of Lie conformal algebras over Integer Ring - """ - if check: - if not (R in _CommutativeRings): - raise ValueError("base must be a commutative ring got {}".format(R)) - return super(LieConformalAlgebras,cls).__classcall__(cls,R) - @cached_method def super_categories(self): """ @@ -213,14 +182,15 @@ def super_categories(self): sage: C = LieConformalAlgebras(QQ) sage: C.super_categories() - [Category of vector spaces over Rational Field] + [Category of Lambda bracket algebras over Rational Field] sage: C = LieConformalAlgebras(QQ).FinitelyGenerated(); C - Category of finitely generated Lie conformal algebras over Rational Field + Category of finitely generated lie conformal algebras over Rational Field sage: C.super_categories() [Category of Lie conformal algebras over Rational Field] sage: C.all_super_categories() - [Category of finitely generated Lie conformal algebras over Rational Field, + [Category of finitely generated lie conformal algebras over Rational Field, Category of Lie conformal algebras over Rational Field, + Category of Lambda bracket algebras over Rational Field, Category of vector spaces over Rational Field, Category of modules over Rational Field, Category of bimodules over Rational Field on the left and Rational Field on the right, @@ -240,7 +210,7 @@ def super_categories(self): Category of sets with partial maps, Category of objects] """ - return [Modules(self.base_ring())] + return [LambdaBracketAlgebras(self.base_ring())] def example(self): """ @@ -268,89 +238,6 @@ def _repr_object_names(self): class ParentMethods: - def ideal(self, *gens, **kwds): - r""" - The ideal of this Lie conformal algebra generated by ``gens``. - - .. TODO:: - - Ideals of Lie Conformal Algebras are not implemented yet. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ideal() - Traceback (most recent call last): - ... - NotImplementedError: ideals of Lie Conformal algebras are not implemented yet - """ - raise NotImplementedError("ideals of Lie Conformal algebras are " - "not implemented yet") - - def is_super(self): - """ - Wether this Lie conformal algebra is a super Lie - conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.is_super() - False - sage: lie_conformal_algebras.NeveuSchwarz(QQbar).is_super() - True - - Notice that we can force to have a *purely even* super Lie - conformal algebra:: - - sage: bosondict = {('a','a'):{1:{('K',0):1}}} - sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',), - ....: central_elements=('K',),super=True) - sage: R.is_super() - True - sage: [g.is_even_odd() for g in R.gens()] - [0, 0] - """ - return self in LieConformalAlgebras(self.base_ring()).Super() - - def is_graded(self): - """ - Wether this Lie conformal algebra is graded or not. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir - The Virasoro Lie conformal algebra over Rational Field - sage: Vir.is_graded() - True - """ - return self in LieConformalAlgebras(self.base_ring()).Graded() - - def is_with_basis(self): - """ - Whether this Lie conformal algebra has a preferred basis. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.is_with_basis() - True - """ - return self in LieConformalAlgebras(self.base_ring()).WithBasis() - - def is_finitely_generated(self): - """ - Whether this Lie conformal algebra is finitely generated. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.is_finitely_generated() - True - """ - return self in LieConformalAlgebras(self.base_ring()).FinitelyGenerated() - def _test_jacobi(self, **options): """ Test the Jacobi axiom of this Lie conformal algebra. @@ -409,8 +296,8 @@ def _test_jacobi(self, **options): for k,br in br2.items(): for j,v in br.items(): for r in range(j+1): - jac2[(k+r, j-r)] = jac2.get((k+r, j-r), pz)\ - + binomial(k+r, r)*v + jac2[(k+r, j-r)] = (jac2.get((k+r, j-r), pz) + + binomial(k+r, r)*v) for k,v in jac2.items(): jac1[k] = jac1.get(k, pz) - v for k,v in jac3.items(): @@ -419,142 +306,6 @@ def _test_jacobi(self, **options): tester.assertDictEqual(jacobiator, {}) class ElementMethods: - @coerce_binop - def bracket(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - EXAMPLES: - - The brackets of the Virasoro Lie conformal algebra:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.bracket(L) - {0: TL, 1: 2*L, 3: 1/2*C} - sage: L.bracket(L.T()) - {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} - - Now with a current algebra:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: E = V.0; H = V.1; F = V.2; - sage: H.bracket(H) - {1: 2*B['K']} - sage: E.bracket(F) - {0: B[alphacheck[1]], 1: B['K']} - """ - return self._bracket_(rhs) - - @abstract_method - def _bracket_(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES: - - The brackets of the Virasoro Lie conformal Algebra:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L._bracket_(L) - {0: TL, 1: 2*L, 3: 1/2*C} - sage: L._bracket_(L.T()) - {0: 2*T^(2)L, 1: 3*TL, 2: 4*L, 4: 2*C} - - Now with a current algebra:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: E = V.0; H = V.1; F = V.2; - sage: H._bracket_(H) - {1: 2*B['K']} - sage: E._bracket_(F) - {0: B[alphacheck[1]], 1: B['K']} - """ - - @coerce_binop - def nproduct(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.nproduct(L, 3) - 1/2*C - sage: L.nproduct(L.T(), 0) - 2*T^(2)L - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: E = V.0; H = V.1; F = V.2; - sage: E.nproduct(H, 0) == - 2*E - True - sage: E.nproduct(F, 1) - B['K'] - """ - return self._nproduct_(rhs,n) - - def _nproduct_(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L._nproduct_(L,3) - 1/2*C - sage: L._nproduct_(L.T(),0) - 2*T^(2)L - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: E = V.0; H = V.1; F = V.2; - sage: E._nproduct_(H,0) == - 2*E - True - sage: E._nproduct_(F,1) - B['K'] - """ - if n >= 0: - return self.bracket(rhs).get(n,self.parent().zero()) - else: - raise NotImplementedError("vertex algebras are not implemented") - - @abstract_method - def T(self, n=1): - r""" - The ``n``-th derivative of ``self``. - - INPUT: - - - ``n`` -- integer (default:``1``); how many times - to apply `T` to this element - - OUTPUT: - - `T^n a` where `a` is this element. Notice that we use the - *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.inject_variables() - Defining L, C - sage: L.T() - TL - sage: L.T(3) - 6*T^(3)L - sage: C.T() - 0 - """ def is_even_odd(self): """ @@ -579,408 +330,15 @@ def is_even_odd(self): """ return 0 - class SubcategoryMethods: - - def FinitelyGeneratedAsLieConformalAlgebra(self): - """ - The category of finitely generated Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).FinitelyGenerated() - Category of finitely generated Lie conformal algebras over Rational Field - """ - return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") - - def FinitelyGenerated(self): - """ - The category of finitely generated Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).FinitelyGenerated() - Category of finitely generated Lie conformal algebras over Rational Field - """ - return self._with_axiom("FinitelyGeneratedAsLieConformalAlgebra") + Graded = LazyImport("sage.categories.graded_lie_conformal_algebras", + "GradedLieConformalAlgebras", "Graded") - class Super(SuperModulesCategory): - """ - The category of super Lie conformal algebras. + Super = LazyImport("sage.categories.super_lie_conformal_algebras", + "SuperLieConformalAlgebras", "Super") - EXAMPLES:: + WithBasis = LazyImport("sage.categories.lie_conformal_algebras_with_basis", + "LieConformalAlgebrasWithBasis", "WithBasis") - sage: LieConformalAlgebras(AA).Super() - Category of super Lie conformal algebras over Algebraic Real Field - """ - def example(self): - """ - An example parent in this category. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).Super().example() - The Neveu-Schwarz super Lie conformal algebra over Rational Field - """ - from sage.algebras.lie_conformal_algebras.neveu_schwarz_lie_conformal_algebra\ - import NeveuSchwarzLieConformalAlgebra - return NeveuSchwarzLieConformalAlgebra(self.base_ring()) - - class ParentMethods: - - def ideal(self, *gens, **kwds): - r""" - The ideal of this super Lie conformal algebra generated - by ``gens``. - - .. TODO:: - - Ideals of super Lie Conformal Algebras are not - implemented yet. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.ideal() - Traceback (most recent call last): - ... - NotImplementedError: ideals of super Lie Conformal algebras are not implemented yet - """ - raise NotImplementedError("ideals of super Lie Conformal algebras " - "are not implemented yet") - - def is_super(self): - """ - This method returns ``True``. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.is_super() - False - sage: lie_conformal_algebras.NeveuSchwarz(QQbar).is_super() - True - - Notice that we can force to have a *purely even* super Lie - conformal algebra:: - - sage: bosondict = {('a','a'):{1:{('K',0):1}}} - sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',), - ....: central_elements=('K',),super=True) - sage: R.is_super() - True - sage: [g.is_even_odd() for g in R.gens()] - [0, 0] - """ - return True - - def is_graded(self): - """ - Wether this super Lie conformal algebra is graded or not. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V - The Neveu-Schwarz super Lie conformal algebra over Rational Field - sage: V.is_graded() - True - """ - return self in LieConformalAlgebras(self.base_ring()).Graded().Super() - - def is_with_basis(self): - """ - Whether this super Lie conformal algebra has a preferred - basis by homogeneous elements. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.is_with_basis() - True - """ - return self in LieConformalAlgebras(self.base_ring()).WithBasis().Super() - - def is_finitely_generated(self): - """ - Whether this super Lie conformal algebra is finitely generated. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.is_finitely_generated() - True - """ - return self in LieConformalAlgebras(self.base_ring()).FinitelyGenerated().Super() - - def _test_jacobi(self, **options): - """ - Test the Jacobi axiom of this super Lie conformal algebra. - - INPUT: - - - ``options`` -- any keyword arguments acceptde by :meth:`_tester` - - EXAMPLES: - - By default, this method tests only the elements returned by - ``self.some_elements()``:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'B2') - sage: V._test_jacobi() # long time (6 seconds) - - It works for super Lie conformal algebras too:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V._test_jacobi() - - We can use specific elements by passing the ``elements`` - keyword argument:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) - sage: V.inject_variables() - Defining e, h, f, K - sage: V._test_jacobi(elements=(e, 2*f+h, 3*h)) - - TESTS:: - - sage: wrongdict = {('a', 'a'): {0: {('b', 0): 1}}, ('b', 'a'): {0: {('a', 0): 1}}} - sage: V = LieConformalAlgebra(QQ, wrongdict, names=('a', 'b'), parity=(1, 0)) - sage: V._test_jacobi() - Traceback (most recent call last): - ... - AssertionError: {(0, 0): -3*a} != {} - - {(0, 0): -3*a} - + {} - """ - tester = self._tester(**options) - S = tester.some_elements() - #Try our best to avoid non-homogeneous elements - elements = [] - for s in S: - try: - s.is_even_odd() - except ValueError: - if tester._instance.is_with_basis(): - elements.extend([s.even_component(),s.odd_component()]) - continue - elements.append(s) - S = elements - from sage.misc.misc import some_tuples - from sage.functions.other import binomial - pz = tester._instance.zero() - for x,y,z in some_tuples(S, 3, tester._max_runs): - if x.is_even_odd()*y.is_even_odd(): - sgn = -1 - else: - sgn = 1 - brxy = x.bracket(y) - brxz = x.bracket(z) - bryz = y.bracket(z) - br1 = {k: x.bracket(v) for k,v in bryz.items()} - br2 = {k: v.bracket(z) for k,v in brxy.items()} - br3 = {k: y.bracket(v) for k,v in brxz.items()} - jac1 = {(j,k): v for k in br1 for j,v in br1[k].items()} - jac3 = {(k,j): v for k in br3 for j,v in br3[k].items()} - jac2 = {} - for k,br in br2.items(): - for j,v in br.items(): - for r in range(j+1): - jac2[(k+r, j-r)] = jac2.get((k+r, j-r), pz)\ - + binomial(k+r, r)*v - for k,v in jac2.items(): - jac1[k] = jac1.get(k, pz) - v - for k,v in jac3.items(): - jac1[k] = jac1.get(k, pz) - sgn*v - jacobiator = {k: v for k,v in jac1.items() if v} - tester.assertDictEqual(jacobiator, {}) - - class ElementMethods: - @coerce_binop - def bracket(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - EXAMPLES: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.bracket(G.T()) - {0: 2*TL, 1: 2*L, 3: 2*C} - """ - return self._bracket_(rhs) - - @abstract_method - def _bracket_(self, rhs): - r""" - The `\lambda`-bracket of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G._bracket_(G.T()) - {0: 2*TL, 1: 2*L, 3: 2*C} - """ - - @coerce_binop - def nproduct(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.nproduct(G.T(), 1) - 2*L - """ - return self._nproduct_(rhs, n) - - def _nproduct_(self, rhs, n): - r""" - The ``n``-th product of these two elements. - - .. NOTE:: - - It is guaranteed that both are elements of the same - parent. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.nproduct(G.T(), 1) - 2*L - """ - if n >= 0: - return self.bracket(rhs).get(n,self.parent().zero()) - else: - raise NotImplementedError("super vertex algebras are not implemented") - - @abstract_method - def T(self, n=1): - r""" - The ``n``-th derivative of ``self``. - - INPUT: - - - ``n`` -- integer (default:``1``); how many times - to apply `T` to this element - - OUTPUT: - - `T^n a` where `a` is this element. Notice that we use the - *divided powers* notation `T^{(j)} = \frac{T^j}{j!}`. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3) - 6*T^(3)G - """ - - @abstract_method - def is_even_odd(self): - """ - Return ``0`` if this element is *even* and ``1`` if it is - *odd*. - - EXAMPLES:: - - sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); - sage: R.inject_variables() - Defining L, G, C - sage: G.is_even_odd() - 1 - """ - - class Graded(GradedModulesCategory): - """ - The subcategory of H-graded super Lie conformal algebras. - - EXAMPLES:: - - sage: C = LieConformalAlgebras(QQbar) - sage: C.Graded().Super() - Category of H-graded super Lie conformal algebras over Algebraic Field - sage: C.Graded().Super() is C.Super().Graded() - True - """ - def _repr_object_names(self): - """ - The names of the objects of this category - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Graded() - Category of H-graded Lie conformal algebras over Algebraic Field - """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) - - class Graded(GradedModulesCategory): - """ - The subcategory of H-graded Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Graded() - Category of H-graded Lie conformal algebras over Algebraic Field - """ - def _repr_object_names(self): - """ - The names of the objects of this category - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).Graded() - Category of H-graded Lie conformal algebras over Algebraic Field - """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) - - class SubcategoryMethods: - def Super(self, base_ring=None): - """ - The category of H-graded super Lie conformal algebras. - - EXAMPLES:: - - sage: LieConformalAlgebras(QQ).Super().Graded() - Category of H-graded super Lie conformal algebras over Rational Field - """ - assert base_ring is None or base_ring is self.base_ring() - return SuperModulesCategory.category_of( - self.base_category()).Graded() - class ElementMethods: - @abstract_method - def degree(self): - """ - The degree of this element. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ); L = Vir.0 - sage: L.degree() - 2 - sage: L.T(3).degree() - 5 - """ - - WithBasis = LazyImport('sage.categories.lie_conformal_algebras_with_basis', - 'LieConformalAlgebrasWithBasis') - - FinitelyGeneratedAsLieConformalAlgebra = LazyImport( + FinitelyGeneratedAsLambdaBracketAlgebra = LazyImport( 'sage.categories.finitely_generated_lie_conformal_algebras', - 'FinitelyGeneratedAsLieConformalAlgebra') + 'FinitelyGeneratedLieConformalAlgebras') diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index 98d0907f8b4..a6c3e195d55 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -17,6 +17,7 @@ #***************************************************************************** from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.graded_lie_conformal_algebras import GradedLieConformalAlgebrasCategory from sage.categories.graded_modules import GradedModulesCategory from sage.categories.super_modules import SuperModulesCategory @@ -67,7 +68,7 @@ class Super(SuperModulesCategory): Category of super Lie conformal algebras with basis over Algebraic Real Field """ class ParentMethods: - + def _even_odd_on_basis(self, m): """ Return the parity of the basis element indexed by ``m``. @@ -114,30 +115,17 @@ def index(self): return next(iter(self.monomial_coefficients())) - class Graded(GradedModulesCategory): + class Graded(GradedLieConformalAlgebrasCategory): """ The category of H-graded super Lie conformal algebras with basis. EXAMPLES:: - sage: C = LieConformalAlgebras(QQbar).WithBasis() - sage: C.Super().Graded() + sage: LieConformalAlgebras(QQbar).WithBasis().Super().Graded() Category of H-graded super Lie conformal algebras with basis over Algebraic Field - sage: C.Super().Graded() is C.Graded().Super() - True """ - def _repr_object_names(self): - """ - The names of the objects of this category. - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).WithBasis().Super().Graded() - Category of H-graded super Lie conformal algebras with basis over Algebraic Field - """ - return "H-graded {}".format(self.base_category()._repr_object_names()) - - class Graded(GradedModulesCategory): + class Graded(GradedLieConformalAlgebrasCategory): """ The category of H-graded Lie conformal algebras with basis. @@ -146,18 +134,8 @@ class Graded(GradedModulesCategory): sage: LieConformalAlgebras(QQbar).WithBasis().Graded() Category of H-graded Lie conformal algebras with basis over Algebraic Field """ - def _repr_object_names(self): - """ - The names of the objects of this category. - - EXAMPLES:: - sage: LieConformalAlgebras(QQbar).WithBasis().Graded() - Category of H-graded Lie conformal algebras with basis over Algebraic Field - """ - return "H-graded {}".format(self.base_category()._repr_object_names()) - - class FinitelyGeneratedAsLieConformalAlgebra(CategoryWithAxiom_over_base_ring): + class FinitelyGeneratedAsLambdaBracketAlgebra(CategoryWithAxiom_over_base_ring): """ The category of finitely generated Lie conformal algebras with basis. @@ -182,8 +160,8 @@ class Super(SuperModulesCategory): """ class Graded(GradedModulesCategory): """ - The category of H-graded finitely generated super - Lie conformal algebras with basis. + The category of H-graded super finitely generated Lie + conformal algebras with basis. EXAMPLES:: @@ -195,18 +173,18 @@ class Graded(GradedModulesCategory): """ def _repr_object_names(self): """ - The names of the objects of this category + The names of the objects of ``self``. EXAMPLES:: - sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Super().Graded() + sage: C = LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated() + sage: C.Super().Graded() Category of H-graded super finitely generated Lie conformal algebras with basis over Algebraic Field """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) + return "H-graded {}".format(self.base_category()._repr_object_names()) class ParentMethods: - + def degree_on_basis(self, m): r""" Return the degree of the basis element indexed by ``m`` @@ -222,7 +200,7 @@ def degree_on_basis(self, m): return 0 return self._weights[self._index_to_pos[m[0]]] + m[1] - class Graded(GradedModulesCategory): + class Graded(GradedLieConformalAlgebrasCategory): """ The category of H-graded finitely generated Lie conformal algebras with basis. @@ -232,18 +210,6 @@ class Graded(GradedModulesCategory): sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field """ - def _repr_object_names(self): - """ - The names of the objects of this category - - EXAMPLES:: - - sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() - Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field - """ - return "H-graded {}".format(self.base_category().\ - _repr_object_names()) - class ParentMethods: def degree_on_basis(self, m): diff --git a/src/sage/categories/super_lie_conformal_algebras.py b/src/sage/categories/super_lie_conformal_algebras.py new file mode 100644 index 00000000000..4ed3d69f709 --- /dev/null +++ b/src/sage/categories/super_lie_conformal_algebras.py @@ -0,0 +1,188 @@ +""" +Super Lie Conformal Algebras + +AUTHORS: + +- Reimundo Heluani (2019-10-05): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.categories.graded_modules import GradedModulesCategory +from sage.categories.super_modules import SuperModulesCategory +from sage.misc.abstract_method import abstract_method +from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras + +class SuperLieConformalAlgebras(SuperModulesCategory): + r""" + The category of super Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).Super() + Category of super Lie conformal algebras over Algebraic Real Field + + Notice that we can force to have a *purely even* super Lie + conformal algebra:: + + sage: bosondict = {('a','a'):{1:{('K',0):1}}} + sage: R = LieConformalAlgebra(QQ,bosondict,names=('a',), + ....: central_elements=('K',), super=True) + sage: [g.is_even_odd() for g in R.gens()] + [0, 0] + """ + def extra_super_categories(self): + """ + The extra super categories of ``self``. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).Super().super_categories() + [Category of super modules over Rational Field, + Category of Lambda bracket algebras over Rational Field] + """ + return [LambdaBracketAlgebras(self.base_ring())] + + def example(self): + """ + An example parent in this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQ).Super().example() + The Neveu-Schwarz super Lie conformal algebra over Rational Field + """ + from sage.algebras.lie_conformal_algebras.neveu_schwarz_lie_conformal_algebra\ + import NeveuSchwarzLieConformalAlgebra + return NeveuSchwarzLieConformalAlgebra(self.base_ring()) + + class ParentMethods: + + def _test_jacobi(self, **options): + """ + Test the Jacobi axiom of this super Lie conformal algebra. + + INPUT: + + - ``options`` -- any keyword arguments acceptde by :meth:`_tester` + + EXAMPLES: + + By default, this method tests only the elements returned by + ``self.some_elements()``:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'B2') + sage: V._test_jacobi() # long time (6 seconds) + + It works for super Lie conformal algebras too:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V._test_jacobi() + + We can use specific elements by passing the ``elements`` + keyword argument:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.inject_variables() + Defining e, h, f, K + sage: V._test_jacobi(elements=(e, 2*f+h, 3*h)) + + TESTS:: + + sage: wrongdict = {('a', 'a'): {0: {('b', 0): 1}}, ('b', 'a'): {0: {('a', 0): 1}}} + sage: V = LieConformalAlgebra(QQ, wrongdict, names=('a', 'b'), parity=(1, 0)) + sage: V._test_jacobi() + Traceback (most recent call last): + ... + AssertionError: {(0, 0): -3*a} != {} + - {(0, 0): -3*a} + + {} + """ + tester = self._tester(**options) + S = tester.some_elements() + # Try our best to avoid non-homogeneous elements + elements = [] + for s in S: + try: + s.is_even_odd() + except ValueError: + try: + elements.extend([s.even_component(), s.odd_component()]) + except (AttributeError, ValueError): + pass + continue + elements.append(s) + S = elements + from sage.misc.misc import some_tuples + from sage.functions.other import binomial + pz = tester._instance.zero() + for x,y,z in some_tuples(S, 3, tester._max_runs): + if x.is_even_odd() * y.is_even_odd(): + sgn = -1 + else: + sgn = 1 + brxy = x.bracket(y) + brxz = x.bracket(z) + bryz = y.bracket(z) + br1 = {k: x.bracket(v) for k,v in bryz.items()} + br2 = {k: v.bracket(z) for k,v in brxy.items()} + br3 = {k: y.bracket(v) for k,v in brxz.items()} + jac1 = {(j,k): v for k in br1 for j,v in br1[k].items()} + jac3 = {(k,j): v for k in br3 for j,v in br3[k].items()} + jac2 = {} + for k,br in br2.items(): + for j,v in br.items(): + for r in range(j+1): + jac2[(k+r, j-r)] = (jac2.get((k+r, j-r), pz) + + binomial(k+r, r)*v) + for k,v in jac2.items(): + jac1[k] = jac1.get(k, pz) - v + for k,v in jac3.items(): + jac1[k] = jac1.get(k, pz) - sgn*v + jacobiator = {k: v for k,v in jac1.items() if v} + tester.assertDictEqual(jacobiator, {}) + + class ElementMethods: + + @abstract_method + def is_even_odd(self): + """ + Return ``0`` if this element is *even* and ``1`` if it is + *odd*. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.NeveuSchwarz(QQ); + sage: R.inject_variables() + Defining L, G, C + sage: G.is_even_odd() + 1 + """ + + class Graded(GradedModulesCategory): + """ + The category of H-graded super Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(AA).Super().Graded() + Category of H-graded super Lie conformal algebras over Algebraic Real Field + """ + def _repr_object_names(self): + """ + The names of the objects of this category. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).Graded() + Category of H-graded Lie conformal algebras over Algebraic Field + """ + return "H-graded {}".format(self.base_category()._repr_object_names()) From 11f70051440a595462af9ca2b1442ebb94bb3243 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Fri, 21 Aug 2020 17:03:00 -0700 Subject: [PATCH 295/379] Add optional pip package: nodeenv --- build/pkgs/nodeenv/SPKG.rst | 25 +++++++++++++++++++++++++ build/pkgs/nodeenv/requirements.txt | 1 + build/pkgs/nodeenv/type | 1 + 3 files changed, 27 insertions(+) create mode 100644 build/pkgs/nodeenv/SPKG.rst create mode 100644 build/pkgs/nodeenv/requirements.txt create mode 100644 build/pkgs/nodeenv/type diff --git a/build/pkgs/nodeenv/SPKG.rst b/build/pkgs/nodeenv/SPKG.rst new file mode 100644 index 00000000000..02d5338dc08 --- /dev/null +++ b/build/pkgs/nodeenv/SPKG.rst @@ -0,0 +1,25 @@ +nodeenv +======= + +Description +----------- + +nodeenv (node.js virtual environment) is a tool to create isolated node.js environments. + +It creates an environment that has its own installation directories, that doesn’t share +libraries with other node.js virtual environments. + +License +------- + +BSD License + +Upstream Contact +---------------- + +Home page: https://github.com/ekalinin/nodeenv + +Dependencies +------------ + +- Python diff --git a/build/pkgs/nodeenv/requirements.txt b/build/pkgs/nodeenv/requirements.txt new file mode 100644 index 00000000000..926d31bf049 --- /dev/null +++ b/build/pkgs/nodeenv/requirements.txt @@ -0,0 +1 @@ +nodeenv ~= 1.4.0 diff --git a/build/pkgs/nodeenv/type b/build/pkgs/nodeenv/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/nodeenv/type @@ -0,0 +1 @@ +optional From 385fe0cf6410f225f5f41ea5c331dc3102688a1a Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Fri, 21 Aug 2020 21:50:01 -0300 Subject: [PATCH 296/379] Moved methods to LambdaBracketAlgebras category --- src/doc/en/reference/categories/index.rst | 3 + ...itely_generated_lambda_bracket_algebras.py | 105 ++++++++++++++++++ ...nitely_generated_lie_conformal_algebras.py | 99 ----------------- .../categories/lambda_bracket_algebras.py | 14 ++- .../lambda_bracket_algebras_with_basis.py | 97 ++++++++++++++++ src/sage/categories/lie_conformal_algebras.py | 4 +- .../lie_conformal_algebras_with_basis.py | 90 --------------- src/sage/categories/super_modules.py | 2 +- 8 files changed, 220 insertions(+), 194 deletions(-) create mode 100644 src/sage/categories/finitely_generated_lambda_bracket_algebras.py create mode 100644 src/sage/categories/lambda_bracket_algebras_with_basis.py diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index c9f4f270628..ab6cb1dbbab 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -95,6 +95,7 @@ Individual Categories sage/categories/finite_semigroups sage/categories/finite_sets sage/categories/finite_weyl_groups + sage/categories/finitely_generated_lambda_bracket_algebras sage/categories/finitely_generated_lie_conformal_algebras sage/categories/finitely_generated_magmas sage/categories/finitely_generated_semigroups @@ -128,6 +129,8 @@ Individual Categories sage/categories/integral_domains sage/categories/j_trivial_semigroups sage/categories/kac_moody_algebras + sage/categories/lambda_bracket_algebras + sage/categories/lambda_bracket_algebras_with_basis sage/categories/lattice_posets sage/categories/left_modules sage/categories/lie_algebras diff --git a/src/sage/categories/finitely_generated_lambda_bracket_algebras.py b/src/sage/categories/finitely_generated_lambda_bracket_algebras.py new file mode 100644 index 00000000000..5d4e1ef3aff --- /dev/null +++ b/src/sage/categories/finitely_generated_lambda_bracket_algebras.py @@ -0,0 +1,105 @@ +""" +Finitely Generated Lambda bracket Algebras + +AUTHORS: + +- Reimundo Heluani (2020-08-21): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.graded_modules import GradedModulesCategory +from sage.categories.super_modules import SuperModulesCategory +from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras + +class FinitelyGeneratedLambdaBracketAlgebras(CategoryWithAxiom_over_base_ring): + """ + The category of finitely generated lambda bracket algebras. + + EXAMPLES:: + + sage: from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras + sage: LambdaBracketAlgebras(QQbar).FinitelyGenerated() + Category of finitely generated lambda bracket algebras over Algebraic Field + """ + _base_category_class_and_axiom = (LambdaBracketAlgebras, "FinitelyGeneratedAsLambdaBracketAlgebra") + class ParentMethods: + def ngens(self): + r""" + The number of generators of this Lie conformal algebra. + + EXAMPLES:: + + sage: Vir = lie_conformal_algebras.Virasoro(QQ) + sage: Vir.ngens() + 2 + + sage: V = lie_conformal_algebras.Affine(QQ, 'A2') + sage: V.ngens() + 9 + """ + return len(self.gens()) + + def gen(self,i): + r""" + The ``i``-th generator of this Lie conformal algebra. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1') + sage: V.gens() + (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) + sage: V.gen(0) + B[alpha[1]] + sage: V.1 + B[alphacheck[1]] + """ + return self.gens()[i] + + def some_elements(self): + """ + Some elements of this Lie conformal algebra. + + This method returns a list with elements containing at + least the generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.some_elements() + [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + + class Graded(GradedModulesCategory): + """ + The category of H-graded finitely generated Lie conformal algebras. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).FinitelyGenerated().Graded() + Category of H-graded finitely generated lie conformal algebras over Algebraic Field + """ + def _repr_object_names(self): + """ + The names of the objects of ``self``. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field + """ + return "H-graded {}".format(self.base_category()._repr_object_names()) diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 116c732d2e6..187e8c8065e 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -31,57 +31,6 @@ class FinitelyGeneratedLieConformalAlgebras(CategoryWithAxiom_over_base_ring): Category of finitely generated lie conformal algebras over Algebraic Field """ _base_category_class_and_axiom = (LieConformalAlgebras, "FinitelyGeneratedAsLambdaBracketAlgebra") - class ParentMethods: - def ngens(self): - r""" - The number of generators of this Lie conformal algebra. - - EXAMPLES:: - - sage: Vir = lie_conformal_algebras.Virasoro(QQ) - sage: Vir.ngens() - 2 - - sage: V = lie_conformal_algebras.Affine(QQ, 'A2') - sage: V.ngens() - 9 - """ - return len(self.gens()) - - def gen(self,i): - r""" - The ``i``-th generator of this Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1') - sage: V.gens() - (B[alpha[1]], B[alphacheck[1]], B[-alpha[1]], B['K']) - sage: V.gen(0) - B[alpha[1]] - sage: V.1 - B[alphacheck[1]] - """ - return self.gens()[i] - - def some_elements(self): - """ - Some elements of this Lie conformal algebra. - - This method returns a list with elements containing at - least the generators. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) - sage: V.some_elements() - [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] - """ - S = list(self.gens()) - from sage.misc.misc import some_tuples - for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): - S.append(x.T() + 2*y.T(2)) - return S class Super(SuperModulesCategory): """ @@ -92,52 +41,6 @@ class Super(SuperModulesCategory): sage: LieConformalAlgebras(AA).FinitelyGenerated().Super() Category of super finitely generated lie conformal algebras over Algebraic Real Field """ - class ParentMethods: - def ngens(self): - r""" - The number of generators of this super Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.ngens() - 3 - """ - return len(self.gens()) - - def gen(self,i): - r""" - The ``i``-th generator of this super Lie conformal algebra. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.gens() - (L, G, C) - sage: V.gen(0) - L - """ - return self.gens()[i] - - def some_elements(self): - """ - Some elements of this super Lie conformal algebra. - - This method returns a list with elements containing at - least the generators. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.some_elements() - [L, G, C, TG + 4*T^(2)L, TG + 4*T^(2)G, TL + 4*T^(2)L] - """ - S = list(self.gens()) - from sage.misc.misc import some_tuples - for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): - S.append(x.T() + 2*y.T(2)) - return S - class Graded(GradedModulesCategory): """ The category of H-graded super finitely generated Lie conformal algebras. @@ -178,5 +81,3 @@ def _repr_object_names(self): Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field """ return "H-graded {}".format(self.base_category()._repr_object_names()) - - diff --git a/src/sage/categories/lambda_bracket_algebras.py b/src/sage/categories/lambda_bracket_algebras.py index ef15e130830..c0f8a00c661 100644 --- a/src/sage/categories/lambda_bracket_algebras.py +++ b/src/sage/categories/lambda_bracket_algebras.py @@ -22,6 +22,7 @@ from sage.structure.element import coerce_binop from sage.misc.cachefunc import cached_method from sage.categories.commutative_rings import CommutativeRings +from sage.misc.lazy_import import LazyImport _CommutativeRings = CommutativeRings() @@ -84,7 +85,7 @@ class SubcategoryMethods: def FinitelyGeneratedAsLambdaBracketAlgebra(self): """ - The category of finitely generated Lie conformal algebras. + The category of finitely generated Lambda bracket algebras. EXAMPLES:: @@ -95,7 +96,7 @@ def FinitelyGeneratedAsLambdaBracketAlgebra(self): def FinitelyGenerated(self): """ - The category of finitely generated Lie conformal algebras. + The category of finitely generated Lambda bracket algebras. EXAMPLES:: @@ -108,7 +109,7 @@ class ParentMethods: def ideal(self, *gens, **kwds): r""" - The ideal of this Lie conformal algebra generated by ``gens``. + The ideal of this Lambda bracket algebra generated by ``gens``. .. TODO:: @@ -262,3 +263,10 @@ def T(self, n=1): sage: C.T() 0 """ + + WithBasis = LazyImport("sage.categories.lambda_bracket_algebras_with_basis", + "LambdaBracketAlgebrasWithBasis", "WithBasis") + + FinitelyGeneratedAsLambdaBracketAlgebra = LazyImport( + 'sage.categories.finitely_generated_lambda_bracket_algebras', + 'FinitelyGeneratedLambdaBracketAlgebras') diff --git a/src/sage/categories/lambda_bracket_algebras_with_basis.py b/src/sage/categories/lambda_bracket_algebras_with_basis.py new file mode 100644 index 00000000000..28f5ef043dd --- /dev/null +++ b/src/sage/categories/lambda_bracket_algebras_with_basis.py @@ -0,0 +1,97 @@ +""" +Lambda Bracket Algebras With Basis + +AUTHORS: + +- Reimundo Heluani (2020-08-21): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.graded_modules import GradedModulesCategory + +class LambdaBracketAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): + """ + The category of Lambda bracket algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis() + Category of Lie conformal algebras with basis over Algebraic Field + """ + class ElementMethods: + + def index(self): + """ + The index of this basis element. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) + sage: V.inject_variables() + Defining L, G, C + sage: G.T(3).index() + ('G', 3) + sage: v = V.an_element(); v + L + G + C + sage: v.index() + Traceback (most recent call last): + ... + ValueError: index can only be computed for monomials, got L + G + C + """ + if self.is_zero(): + return None + if not self.is_monomial(): + raise ValueError ("index can only be computed for " + "monomials, got {}".format(self)) + + return next(iter(self.monomial_coefficients())) + + class FinitelyGeneratedAsLambdaBracketAlgebra(CategoryWithAxiom_over_base_ring): + """ + The category of finitely generated lambda bracket algebras with + basis. + + EXAMPLES:: + + sage: C = LieConformalAlgebras(QQbar) + sage: C.WithBasis().FinitelyGenerated() + Category of finitely generated Lie conformal algebras with basis over Algebraic Field + sage: C.WithBasis().FinitelyGenerated() is C.FinitelyGenerated().WithBasis() + True + """ + class Graded(GradedModulesCategory): + """ + The category of H-graded finitely generated lambda bracket + algebras with basis. + + EXAMPLES:: + + sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field + """ + class ParentMethods: + + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Virasoro(QQ) + sage: V.degree_on_basis(('L',2)) + 4 + """ + if m[0] in self._central_elements: + return 0 + return self._weights[self._index_to_pos[m[0]]] + m[1] diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index c889444cb56..bd003e1f8f6 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -186,9 +186,11 @@ def super_categories(self): sage: C = LieConformalAlgebras(QQ).FinitelyGenerated(); C Category of finitely generated lie conformal algebras over Rational Field sage: C.super_categories() - [Category of Lie conformal algebras over Rational Field] + [Category of finitely generated lambda bracket algebras over Rational Field, + Category of Lie conformal algebras over Rational Field] sage: C.all_super_categories() [Category of finitely generated lie conformal algebras over Rational Field, + Category of finitely generated lambda bracket algebras over Rational Field, Category of Lie conformal algebras over Rational Field, Category of Lambda bracket algebras over Rational Field, Category of vector spaces over Rational Field, diff --git a/src/sage/categories/lie_conformal_algebras_with_basis.py b/src/sage/categories/lie_conformal_algebras_with_basis.py index a6c3e195d55..50f67d2a970 100644 --- a/src/sage/categories/lie_conformal_algebras_with_basis.py +++ b/src/sage/categories/lie_conformal_algebras_with_basis.py @@ -30,34 +30,6 @@ class LieConformalAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): sage: LieConformalAlgebras(QQbar).WithBasis() Category of Lie conformal algebras with basis over Algebraic Field """ - class ElementMethods: - - def index(self): - """ - The index of this basis element. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3).index() - ('G', 3) - sage: v = V.an_element(); v - L + G + C - sage: v.index() - Traceback (most recent call last): - ... - ValueError: index can only be computed for monomials, got L + G + C - """ - if self.is_zero(): - return None - if not self.is_monomial(): - raise ValueError ("index can only be computed for " - "monomials, got {}".format(self)) - - return next(iter(self.monomial_coefficients())) - class Super(SuperModulesCategory): """ The category of super Lie conformal algebras with basis. @@ -87,34 +59,6 @@ def _even_odd_on_basis(self, m): """ return self._parity[self.monomial((m[0],0))] - class ElementMethods: - - def index(self): - """ - The index of this basis element. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.NeveuSchwarz(QQ) - sage: V.inject_variables() - Defining L, G, C - sage: G.T(3).index() - ('G', 3) - sage: v = V.an_element(); v - L + G + C - sage: v.index() - Traceback (most recent call last): - ... - ValueError: index can only be computed for monomials, got L + G + C - """ - if self.is_zero(): - return None - if not self.is_monomial(): - raise ValueError ("index can only be computed for " - "monomials, got {}".format(self)) - - return next(iter(self.monomial_coefficients())) - class Graded(GradedLieConformalAlgebrasCategory): """ The category of H-graded super Lie conformal algebras with basis. @@ -183,23 +127,6 @@ def _repr_object_names(self): """ return "H-graded {}".format(self.base_category()._repr_object_names()) - class ParentMethods: - - def degree_on_basis(self, m): - r""" - Return the degree of the basis element indexed by ``m`` - in ``self``. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.degree_on_basis(('L',2)) - 4 - """ - if m[0] in self._central_elements: - return 0 - return self._weights[self._index_to_pos[m[0]]] + m[1] - class Graded(GradedLieConformalAlgebrasCategory): """ The category of H-graded finitely generated Lie conformal @@ -210,20 +137,3 @@ class Graded(GradedLieConformalAlgebrasCategory): sage: LieConformalAlgebras(QQbar).WithBasis().FinitelyGenerated().Graded() Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Field """ - class ParentMethods: - - def degree_on_basis(self, m): - r""" - Return the degree of the basis element indexed by ``m`` - in ``self``. - - EXAMPLES:: - - sage: V = lie_conformal_algebras.Virasoro(QQ) - sage: V.degree_on_basis(('L',2)) - 4 - """ - if m[0] in self._central_elements: - return 0 - return self._weights[self._index_to_pos[m[0]]] + m[1] - diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index b7f15241c86..945122e6e09 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -15,7 +15,7 @@ # therefore the following whitelist. axiom_whitelist = frozenset(["Facade", "Finite", "Infinite", "FiniteDimensional", "Connected", "WithBasis", - "FinitelyGeneratedAsLieConformalAlgebra", + "FinitelyGeneratedAsLambdaBracketAlgebra", # "Commutative", "Cocommutative", "Supercommutative", "Supercocommutative", "Associative", "Inverse", "Unital", "Division", From 201b57e14877bfd97c9a59dd108aff2f16c1d87a Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Fri, 21 Aug 2020 19:57:49 -0700 Subject: [PATCH 297/379] Add optional package: jupyterlab --- build/pkgs/jupyterlab/SPKG.rst | 26 ++++++++++++++++++++++++++ build/pkgs/jupyterlab/dependencies | 5 +++++ build/pkgs/jupyterlab/requirements.txt | 1 + build/pkgs/jupyterlab/type | 1 + 4 files changed, 33 insertions(+) create mode 100644 build/pkgs/jupyterlab/SPKG.rst create mode 100644 build/pkgs/jupyterlab/dependencies create mode 100644 build/pkgs/jupyterlab/requirements.txt create mode 100644 build/pkgs/jupyterlab/type diff --git a/build/pkgs/jupyterlab/SPKG.rst b/build/pkgs/jupyterlab/SPKG.rst new file mode 100644 index 00000000000..3f1f1cb6fed --- /dev/null +++ b/build/pkgs/jupyterlab/SPKG.rst @@ -0,0 +1,26 @@ +JupyterLab +========== + +Description +----------- + +An extensible environment for interactive and reproducible computing, +based on the Jupyter Notebook and Architecture. + +License +------- + +BSD License + +Upstream Contact +---------------- + +Home page: http://jupyter.org/ + +Dependencies +------------ + +- Python +- Setuptools +- jupyter_core +- jupyter_client diff --git a/build/pkgs/jupyterlab/dependencies b/build/pkgs/jupyterlab/dependencies new file mode 100644 index 00000000000..873767864f2 --- /dev/null +++ b/build/pkgs/jupyterlab/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) vcversioner jupyter_core jupyter_client | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/jupyterlab/requirements.txt b/build/pkgs/jupyterlab/requirements.txt new file mode 100644 index 00000000000..971a1cf8b05 --- /dev/null +++ b/build/pkgs/jupyterlab/requirements.txt @@ -0,0 +1 @@ +jupyterlab ~= 2.2.5 diff --git a/build/pkgs/jupyterlab/type b/build/pkgs/jupyterlab/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/jupyterlab/type @@ -0,0 +1 @@ +optional From ff392d8225640446d275a3913fab8adcd0c352ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 21 Aug 2020 20:23:52 -0700 Subject: [PATCH 298/379] build/pkgs/cmake: Update to 3.18.2 --- build/pkgs/cmake/checksums.ini | 6 +++--- build/pkgs/cmake/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cmake/checksums.ini b/build/pkgs/cmake/checksums.ini index 5d281924e17..84c9987a0f2 100644 --- a/build/pkgs/cmake/checksums.ini +++ b/build/pkgs/cmake/checksums.ini @@ -1,5 +1,5 @@ tarball=cmake-VERSION.tar.gz -sha1=faef76cfc9f07cae7eecee72eba0d28abbbac5b4 -md5=d47f23a9781b68014e77717f8e291bb7 -cksum=4044419587 +sha1=23092c6a5ae9ecde368abbfda7bae0d01edb7387 +md5=7a882b3764f42981705286ac9daa29c2 +cksum=1249294118 upstream_url=https://github.com/Kitware/CMake/releases/download/vVERSION/cmake-VERSION.tar.gz diff --git a/build/pkgs/cmake/package-version.txt b/build/pkgs/cmake/package-version.txt index 56cc1b61f82..9f6a62d00b5 100644 --- a/build/pkgs/cmake/package-version.txt +++ b/build/pkgs/cmake/package-version.txt @@ -1 +1 @@ -3.17.3 +3.18.2 From bec2e2c9a4a92d2cd5950fa7fe3c9b6f447ffaf1 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sat, 22 Aug 2020 06:27:17 -0300 Subject: [PATCH 299/379] Remove unused import and some blank lines --- .../categories/finitely_generated_lambda_bracket_algebras.py | 1 - src/sage/categories/graded_lie_conformal_algebras.py | 1 - src/sage/categories/lie_conformal_algebras.py | 1 - 3 files changed, 3 deletions(-) diff --git a/src/sage/categories/finitely_generated_lambda_bracket_algebras.py b/src/sage/categories/finitely_generated_lambda_bracket_algebras.py index 5d4e1ef3aff..c491d58cd67 100644 --- a/src/sage/categories/finitely_generated_lambda_bracket_algebras.py +++ b/src/sage/categories/finitely_generated_lambda_bracket_algebras.py @@ -18,7 +18,6 @@ from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.categories.graded_modules import GradedModulesCategory -from sage.categories.super_modules import SuperModulesCategory from sage.categories.lambda_bracket_algebras import LambdaBracketAlgebras class FinitelyGeneratedLambdaBracketAlgebras(CategoryWithAxiom_over_base_ring): diff --git a/src/sage/categories/graded_lie_conformal_algebras.py b/src/sage/categories/graded_lie_conformal_algebras.py index 885caac3870..66950ecce53 100644 --- a/src/sage/categories/graded_lie_conformal_algebras.py +++ b/src/sage/categories/graded_lie_conformal_algebras.py @@ -68,4 +68,3 @@ class GradedLieConformalAlgebras(GradedLieConformalAlgebrasCategory): sage: CS is LieConformalAlgebras(QQ).Super().Graded() True """ - diff --git a/src/sage/categories/lie_conformal_algebras.py b/src/sage/categories/lie_conformal_algebras.py index bd003e1f8f6..8f4725e3c47 100644 --- a/src/sage/categories/lie_conformal_algebras.py +++ b/src/sage/categories/lie_conformal_algebras.py @@ -172,7 +172,6 @@ class LieConformalAlgebras(Category_over_base_ring): Traceback (most recent call last): ValueError: base must be a commutative ring got Quaternion Algebra (-1, -1) with base ring Rational Field """ - @cached_method def super_categories(self): """ From 0f39045d56781b1e5f278768c9655c75ab1116e0 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sat, 22 Aug 2020 06:42:00 -0300 Subject: [PATCH 300/379] Relocated repr_lincomb import --- .../lie_conformal_algebras/lie_conformal_algebra_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index de30de070f6..3132ea64dbe 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -19,7 +19,7 @@ from sage.functions.other import factorial from sage.misc.misc_c import prod -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.misc.latex import latex from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement From c603942d68a7ff97268f0a998cc0ff52b90dc634 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 22 Aug 2020 12:40:17 +0200 Subject: [PATCH 301/379] trac #30394: review commit --- src/sage/graphs/graph.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 08e416c1e93..9ccb96e9707 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -9365,7 +9365,7 @@ def arboricity(self, certificate=False): @doc_index("Graph properties") def is_antipodal(self): r""" - Return whether this graph is antipodal. + Check whether this graph is antipodal. A graph `G` of diameter `d` is said to be antipodal if its distance-`d` graph is a disjoint union of cliques. @@ -9500,8 +9500,7 @@ def folded_graph(self, check=False): G = self.antipodal_graph() vertices = set(G) - newVertices = {} - numCliques = 0 + newVertices = [] while vertices: v = vertices.pop() clique = frozenset(G.neighbor_iterator(v, closed=True)) @@ -9511,11 +9510,11 @@ def folded_graph(self, check=False): if frozenset(G.neighbor_iterator(u, closed=True)) != clique: return False - newVertices[numCliques] = clique - numCliques += 1 + newVertices.append(clique) vertices.difference_update(clique) # now newVertices is a map {0, ..., numCliques-1} -> antipodal classes + numCliques = len(newVertices) edges = [] for i, j in itertools.combinations(range(numCliques), 2): if any(self.has_edge(u, v) for u, v in @@ -9532,9 +9531,9 @@ def antipodal_graph(self): r""" Return the antipodal graph of ``self``. - The antipodal graph of `G` has the same vertex set of `G` and two - vertices are adjacent if their distance in `G` is equal to the diameter - of `G`. + The antipodal graph of a graph `G` has the same vertex set of `G` and + two vertices are adjacent if their distance in `G` is equal to the + diameter of `G`. OUTPUT: @@ -9571,7 +9570,6 @@ def antipodal_graph(self): sage: G.antipodal_graph() Antipodal graph of Graph: Looped graph on 1 vertex """ - H = self.distance_graph(self.diameter()) name = self.name() if self.name() != "" else "Graph" From fdd9ae574c058bef5d9efbcb4eb0effdaccc1370 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 22 Aug 2020 15:06:04 +0200 Subject: [PATCH 302/379] renamed method and file; some pep8 --- src/sage/combinat/designs/design_catalog.py | 2 +- ...es.pyx => gen_quadrangles_with_spread.pyx} | 54 ++++++++++--------- 2 files changed, 30 insertions(+), 26 deletions(-) rename src/sage/combinat/designs/{gen_quadrangles.pyx => gen_quadrangles_with_spread.pyx} (86%) diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index a31545a6079..3c6237cffca 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -118,4 +118,4 @@ 'OAMainFunctions', as_='orthogonal_arrays') lazy_import('sage.combinat.designs.gen_quadrangles', - ('generalised_quadrangle_with_spread', 'generalised_quadrangle_hermitian')) + ('generalised_quadrangle_with_spread', 'generalised_quadrangle_hermitian_with_ovoid')) diff --git a/src/sage/combinat/designs/gen_quadrangles.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx similarity index 86% rename from src/sage/combinat/designs/gen_quadrangles.pyx rename to src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index f10d890c02a..059b4d03fbe 100644 --- a/src/sage/combinat/designs/gen_quadrangles.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -1,5 +1,5 @@ r""" -Database of generalised quadrangles +Database of generalised quadrangles with spread This module implements some construction of generalised quadrangles with spread. @@ -23,6 +23,8 @@ REFERENCES: - [PT2009]_ - [TP1994]_ + +- :wikipedia:`Generalized_quadrangle` AUTHORS: @@ -105,10 +107,10 @@ def generalised_quadrangle_with_spread(const int s, const int t, D = IncidenceStructure([[0, 1], [1, 2], [2, 3], [3, 0]]) return (D, [[0, 1], [2, 3]]) - if is_prime_power(s) and t == s*s: + if is_prime_power(s) and t == s * s: if existence: return True - (GQ, S) = dual_GQ_ovoid(*generalised_quadrangle_hermitian(s)) + (GQ, S) = dual_GQ_ovoid(*generalised_quadrangle_hermitian_with_ovoid(s)) if check: if not is_GQ_with_spread(GQ, S, s=s, t=t): raise RuntimeError("Sage built a wrong GQ with spread") @@ -138,7 +140,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): EXAMPLES:: sage: from sage.combinat.designs.gen_quadrangles import * - sage: t = generalised_quadrangle_hermitian(3) + sage: t = generalised_quadrangle_hermitian_with_ovoid(3) sage: is_GQ_with_spread(*t) Traceback (most recent call last): ... @@ -152,7 +154,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): TESTS:: sage: from sage.combinat.designs.gen_quadrangles import * - sage: t = generalised_quadrangle_hermitian(2) + sage: t = generalised_quadrangle_hermitian_with_ovoid(2) sage: t = dual_GQ_ovoid(*t) sage: is_GQ_with_spread(*t, s=2, t=4) True @@ -200,7 +202,7 @@ def dual_GQ_ovoid(GQ,O): EXAMPLES:: sage: from sage.combinat.designs.gen_quadrangles import dual_GQ_ovoid - sage: t = designs.generalised_quadrangle_hermitian(3) + sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(3) sage: t[0].is_generalized_quadrangle(parameters=True) (9, 3) sage: t = dual_GQ_ovoid(*t) @@ -213,7 +215,7 @@ def dual_GQ_ovoid(GQ,O): TESTS:: sage: from sage.combinat.designs.gen_quadrangles import * - sage: t = designs.generalised_quadrangle_hermitian(2) + sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(2) sage: t = dual_GQ_ovoid(*t) sage: t[0].is_generalized_quadrangle(parameters=True) (2, 4) @@ -237,7 +239,7 @@ def dual_GQ_ovoid(GQ,O): D = IncidenceStructure(newBlocks) return (D, S) -def generalised_quadrangle_hermitian(const int q): +def generalised_quadrangle_hermitian_with_ovoid(const int q): r""" Construct the generalised quadrangle `H(3,q^2)` with an ovoid. @@ -255,7 +257,7 @@ def generalised_quadrangle_hermitian(const int q): EXAMPLES:: - sage: t = designs.generalised_quadrangle_hermitian(4) + sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(4) sage: t[0] Incidence structure with 1105 points and 325 blocks sage: len(t[1]) @@ -272,11 +274,12 @@ def generalised_quadrangle_hermitian(const int q): sage: from sage.combinat.designs.gen_quadrangles import \ is_GQ_with_spread, dual_GQ_ovoid - sage: t = designs.generalised_quadrangle_hermitian(3) + sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(3) sage: t = dual_GQ_ovoid(*t) sage: is_GQ_with_spread(*t, s=3, t=9) True - sage: t = dual_GQ_ovoid(*(designs.generalised_quadrangle_hermitian(2))) + sage: t = dual_GQ_ovoid(*( + ....: designs.generalised_quadrangle_hermitian_with_ovoid(2))) sage: t[0] Incidence structure with 27 points and 45 blocks sage: len(t[1]) @@ -286,39 +289,39 @@ def generalised_quadrangle_hermitian(const int q): from sage.combinat.designs.incidence_structures import IncidenceStructure from sage.arith.misc import is_prime_power - GU = libgap.GU(4,q) + GU = libgap.GU(4, q) H = libgap.InvariantSesquilinearForm(GU)["matrix"] - Fq = libgap.GF(q*q) + Fq = libgap.GF(q * q) zero = libgap.Zero(Fq) one = libgap.One(Fq) - V = libgap.FullRowSpace(Fq,4) + V = libgap.FullRowSpace(Fq, 4) - e1 = [one,zero,zero,zero] # isotropic point + e1 = [one, zero, zero, zero] # isotropic point - points = list(libgap.Orbit(GU,e1,libgap.OnLines)) # all isotropic points - pointInt = { x:(i+1) for i,x in enumerate(points) } + points = list(libgap.Orbit(GU, e1, libgap.OnLines)) # all isotropic points + pointInt = { x:(i + 1) for i, x in enumerate(points) } # above we sum 1 because GAP starts at 1 GUp = libgap.Action(GU, points, libgap.OnLines) - e2 = [zero,one,zero,zero] # another isotropic point - line = V.Subspace([e1,e2]) # totally isotropic line + e2 = [zero, one, zero, zero] # another isotropic point + line = V.Subspace([e1, e2]) # totally isotropic line lineAsPoints = [libgap.Elements(libgap.Basis(b))[0] for b in libgap.Elements(line.Subspaces(1))] line = libgap.Set([pointInt[p] for p in lineAsPoints]) lines = libgap.Orbit(GUp, line, libgap.OnSets) # all isotropic lines - lines = [list(map(lambda x: int(x-1), b)) for b in lines] # convert to int - # lines defines the GQ H(3,q^2) + lines = [list(map(lambda x: int(x - 1), b)) for b in lines] # convert to int + # lines defines the GQ H(3, q^2) # to find an ovoid, we embed H(3,q^2) in H(4,q^2) # the embedding is (a,b,c,d) -> (a,b,0,c,d) # then we find a point in the latter and not in the former # this point will be collinear (in H(3,q^2)) to all points in an ovoid - if q%2 == 1: - (p,k) = is_prime_power(q, get_data=True) - a = (p-1)// 2 + if q % 2 == 1: + (p, k) = is_prime_power(q, get_data=True) + a = (p-1) // 2 aGap = zero for i in range(a): aGap += one @@ -327,7 +330,8 @@ def generalised_quadrangle_hermitian(const int q): a = libgap.PrimitiveRoot(Fq)**(q-1) p = [zero, one, a+one, a, zero] - J = [[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]] + J = [[0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], + [0, 1, 0, 0, 0], [1, 0, 0, 0, 0]] J = libgap(J) # matrix of the invariant form of GU(5,q) # p' is collinear to p iff p'Jp^q = 0 From 7f5dc6f7f9f5a2f202fcc0cb43710d4ca64b4136 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 22 Aug 2020 15:12:26 +0200 Subject: [PATCH 303/379] fix bugs due to renaming --- src/sage/combinat/designs/design_catalog.py | 2 +- .../designs/gen_quadrangles_with_spread.pyx | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 3c6237cffca..3a3c11597a9 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -117,5 +117,5 @@ lazy_import('sage.combinat.designs.orthogonal_arrays', 'OAMainFunctions', as_='orthogonal_arrays') -lazy_import('sage.combinat.designs.gen_quadrangles', +lazy_import('sage.combinat.designs.gen_quadrangles_with_spread', ('generalised_quadrangle_with_spread', 'generalised_quadrangle_hermitian_with_ovoid')) diff --git a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index 059b4d03fbe..5ea688999dd 100644 --- a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -9,7 +9,7 @@ EXAMPLES:: sage: GQ, S = designs.generalised_quadrangle_with_spread(4, 16, check=False) sage: GQ Incidence structure with 325 points and 1105 blocks - sage: GQ2, O = designs.generalised_quadrangle_hermitian(4) + sage: GQ2, O = designs.generalised_quadrangle_hermitian_with_ovoid(4) sage: GQ2 Incidence structure with 1105 points and 325 blocks sage: GQ3 = GQ.dual() @@ -139,7 +139,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): EXAMPLES:: - sage: from sage.combinat.designs.gen_quadrangles import * + sage: from sage.combinat.designs.gen_quadrangles_with_spread import * sage: t = generalised_quadrangle_hermitian_with_ovoid(3) sage: is_GQ_with_spread(*t) Traceback (most recent call last): @@ -153,7 +153,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): TESTS:: - sage: from sage.combinat.designs.gen_quadrangles import * + sage: from sage.combinat.designs.gen_quadrangles_with_spread import * sage: t = generalised_quadrangle_hermitian_with_ovoid(2) sage: t = dual_GQ_ovoid(*t) sage: is_GQ_with_spread(*t, s=2, t=4) @@ -201,7 +201,8 @@ def dual_GQ_ovoid(GQ,O): EXAMPLES:: - sage: from sage.combinat.designs.gen_quadrangles import dual_GQ_ovoid + sage: from sage.combinat.designs.gen_quadrangles_with_spread import \ + ....: dual_GQ_ovoid sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(3) sage: t[0].is_generalized_quadrangle(parameters=True) (9, 3) @@ -214,7 +215,7 @@ def dual_GQ_ovoid(GQ,O): TESTS:: - sage: from sage.combinat.designs.gen_quadrangles import * + sage: from sage.combinat.designs.gen_quadrangles_with_spread import * sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(2) sage: t = dual_GQ_ovoid(*t) sage: t[0].is_generalized_quadrangle(parameters=True) @@ -272,7 +273,7 @@ def generalised_quadrangle_hermitian_with_ovoid(const int q): TESTS:: - sage: from sage.combinat.designs.gen_quadrangles import \ + sage: from sage.combinat.designs.gen_quadrangles_with_spread import \ is_GQ_with_spread, dual_GQ_ovoid sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(3) sage: t = dual_GQ_ovoid(*t) From 9a56f1eee61fc90a277e16d9cfec2b5eb96e68c8 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 22 Aug 2020 15:38:41 +0200 Subject: [PATCH 304/379] added is_spread method to incidence structures --- .../designs/gen_quadrangles_with_spread.pyx | 11 +---- .../combinat/designs/incidence_structures.py | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index 5ea688999dd..37b2f54d671 100644 --- a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -171,16 +171,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): return False # check spread - points = set(GQ.ground_set()) - for line in S: - if not points.issuperset(line): - return False - points = points.difference(line) - - if points: - return False - - return True + return GQ.is_spread(S) def dual_GQ_ovoid(GQ,O): r""" diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 12eb87c6c31..3f3b295cac8 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -2232,6 +2232,53 @@ def _latex_(self): tex += "\\end{tikzpicture}" return tex + def is_spread(self, spread): + r""" + Check whether the input is a spread for ``self``. + + A spread of an incidence structure `(P, B)` is a set of blocks which + forms a partition of `P`. + + INPUT: + + - ``spread`` -- iterable; defines the spread + + EXAMPLES:: + + sage: E = IncidenceStructure([[1, 2, 3], [4, 5, 6], [1, 5, 6]]) + sage: E.is_spread([[1, 2, 3], [4, 5, 6]]) + True + sage: E.is_spread([1, 2, 3, 4, 5, 6]) + Traceback (most recent call last): + ... + TypeError: 'sage.rings.integer.Integer' object is not iterable + + TESTS:: + + sage: E = IncidenceStructure([]) + sage: E.is_spread([]) + True + sage: E = IncidenceStructure([[1]]) + sage: E.is_spread([]) + False + sage: E.is_spread([[1]]) + True + sage: E = IncidenceStructure([[1], [1]]) + sage: E.is_spread([[1]]) + True + """ + + points = set(self.ground_set()) + for block in spread: + if not points.issuperset(block): + return False + points.difference_update(block) + + if points: + return False + + return True + from sage.misc.rest_index_of_methods import gen_rest_table_index __doc__ = __doc__.format(METHODS_OF_IncidenceStructure=gen_rest_table_index(IncidenceStructure)) From a464a9c08d164470bb744ded7bdb386cbc79e62b Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 22 Aug 2020 15:51:31 +0200 Subject: [PATCH 305/379] stronger check for spread --- .../designs/gen_quadrangles_with_spread.pyx | 4 ++-- .../combinat/designs/incidence_structures.py | 21 ++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index 37b2f54d671..645e43b4a33 100644 --- a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -173,7 +173,7 @@ def is_GQ_with_spread(GQ, S, s=None, t=None): # check spread return GQ.is_spread(S) -def dual_GQ_ovoid(GQ,O): +def dual_GQ_ovoid(GQ, O): r""" Compute the dual incidence structure of GQ and return the image of `O` under the dual map @@ -291,7 +291,7 @@ def generalised_quadrangle_hermitian_with_ovoid(const int q): e1 = [one, zero, zero, zero] # isotropic point points = list(libgap.Orbit(GU, e1, libgap.OnLines)) # all isotropic points - pointInt = { x:(i + 1) for i, x in enumerate(points) } + pointInt = { x: int(i + 1) for i, x in enumerate(points) } # above we sum 1 because GAP starts at 1 GUp = libgap.Action(GU, points, libgap.OnLines) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 3f3b295cac8..632be4de0ec 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -2236,7 +2236,7 @@ def is_spread(self, spread): r""" Check whether the input is a spread for ``self``. - A spread of an incidence structure `(P, B)` is a set of blocks which + A spread of an incidence structure `(P, B)` is a subset of `B` which forms a partition of `P`. INPUT: @@ -2252,6 +2252,14 @@ def is_spread(self, spread): Traceback (most recent call last): ... TypeError: 'sage.rings.integer.Integer' object is not iterable + sage: E.is_spread([[1, 2, 3, 4], [5, 6]]) + False + + Order of blocks or of points within each block doesn't matter:: + + sage: E = IncidenceStructure([[1, 2, 3], [4, 5, 6], [1, 5, 6]]) + sage: E.is_spread([[5, 6, 4], [3, 1, 2]]) + True TESTS:: @@ -2269,10 +2277,17 @@ def is_spread(self, spread): """ points = set(self.ground_set()) + allBlocks = set(map(frozenset, self.blocks())) for block in spread: - if not points.issuperset(block): + sblock = set(block) + + if sblock not in allBlocks: + return False + + if not points.issuperset(sblock): return False - points.difference_update(block) + + points.difference_update(sblock) if points: return False From 01938b0c74de9e625aa4c8ba803b1d06d93c79ee Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sat, 22 Aug 2020 12:35:33 -0400 Subject: [PATCH 306/379] Added more documentation. Fixed bug the incorrect ribbons being chosen. Merged mobile example branch --- src/sage/combinat/posets/linear_extensions.py | 11 +- src/sage/combinat/posets/mobile.py | 113 +++++++++++------- src/sage/combinat/posets/poset_examples.py | 6 +- 3 files changed, 86 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index e874b513cef..ed630aa6894 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -913,6 +913,12 @@ def cardinality(self): sage: M1 = posets.RibbonPoset(6, [1,3]) sage: M1.linear_extensions().cardinality() 61 + + sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), {1: + ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, + ....: anchor=(4, 2, posets.ChainPoset(6))) + sage: P.linear_extensions().cardinality() + 361628701868606400 """ import sage.combinat.posets.d_complete as dc import sage.combinat.posets.posets as fp @@ -932,8 +938,11 @@ def cardinality(self): fold_down.append((r, self._poset._ribbon[ind+1])) folds = fold_up + fold_down - # Get ordered connected components + if len(folds) == 0: + return dc.DCompletePoset(self._poset).linear_extensions().cardinality() + + # Get ordered connected components cr = self._poset.cover_relations() foldless_cr = [tuple(c) for c in cr if tuple(c) not in folds] diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index 60a9a6abca1..7333679829e 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -30,58 +30,85 @@ class MobilePoset(FinitePoset): def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None): r""" + Initialize self. The second example comes from Example 5.9 in [GGMM2020]_ + EXAMPLES:: - sage: P = posets.Mobile(posets.RibbonPoset(7, [1,3]), {1: + sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), {1: ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, ....: anchor=(4, 2, posets.ChainPoset(6))) - sage: type(P) - - - The internal data structure consists of: - - - the data required to form a finite poset (see - :class:`sage.combinat.posets.posets.FinitePoset`):: - - - ``ribbon`` -- an ordered list of elements that are a path in the - undirected Hasse diagram and form the ribbon of the mobile - - TESTS:: - - sage: P._ribbon - [(4, 5), (4, 4), (4, 3), (4, 2), 4, 3, 2, 1] + sage: len(P._ribbon) + 8 sage: P._anchor (4, 5) - sage: P.linear_extensions().cardinality() - 361628701868606400 - - See also the tests in the class documentation. + sage: P1 = posets.MobilePoset(posets.RibbonPoset(8, [2,3,4]), + ....: {4: [posets.ChainPoset(1)]}, anchor=(3, 0, posets.ChainPoset(1))) + sage: sorted([P1._element_to_vertex(i) for i in P1._ribbon]) + [0, 1, 2, 6, 7, 9] + sage: P1._anchor + (3, 2) + + sage: P2 = posets.MobilePoset(posets.RibbonPoset(15, [1,3,5,7,9,11,13]), + ....: {}, anchor=(8, 0, posets.ChainPoset(1))) + sage: sorted(P2._ribbon) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + sage: P2._anchor + (8, (8, 0)) + sage: P2.linear_extensions().cardinality() + 21399440939 + + sage: TestSuite(P2).run() """ FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, category=category, facade=facade, key=key) - if ribbon and self._test_valid_ribbon(ribbon): + if ribbon and self._is_valid_ribbon(ribbon): self._ribbon = ribbon - def _test_valid_ribbon(self, ribbon): + def _is_valid_ribbon(self, ribbon): r""" - Returns True if a ribbon has at most one anchor and that no vertex has two or more anchors + Return True if a ribbon has at most one anchor, no vertex has two or more anchors, + and every hanging poset is d-complete. INPUT: - ``ribbon`` -- a list of elements that form a ribbon in your poset + + TESTS:: + + sage: P = posets.RibbonPoset(5, [2]) + sage: P._is_valid_ribbon([0,1,2,3,4]) + True + sage: P._is_valid_ribbon([2]) + False + sage: P._is_valid_ribbon([2,3,4]) + True + sage: P._is_valid_ribbon([2,3]) + True """ - ribbon = list(map(lambda x: self._element_to_vertex(x), ribbon)) - H = self._hasse_diagram - R = H.subgraph(ribbon) + ribbon = [self._element_to_vertex(x) for x in ribbon] + G = self._hasse_diagram + G_un = G.to_undirected().copy(immutable=False) + R = G.subgraph(ribbon) num_anchors = 0 + for r in ribbon: - anchor_neighbors = set(H.neighbors_out(r)).difference(set(R.neighbors_out(r))) + anchor_neighbors = set(G.neighbors_out(r)).difference(set(R.neighbors_out(r))) if len(anchor_neighbors) == 1: num_anchors += 1 elif len(anchor_neighbors) > 1: return False - return num_anchors <= 1 + for lc in G.neighbors_in(r): + if lc in ribbon: + continue + + G_un.delete_edge(lc, r) + P = Poset(G.subgraph(G_un.connected_component_containing_vertex(lc))) + if P.top() != lc or not P.is_d_complete(): + return False + G_un.add_edge(lc, r) + + return True @lazy_attribute @@ -92,10 +119,12 @@ def _anchor(self): TESTS:: sage: from sage.combinat.posets.mobile import MobilePoset - sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) + sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], + ....: [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: M._anchor (4, 3) - sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], + ....: [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) sage: M2._anchor (4, 3) sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) @@ -129,20 +158,20 @@ def _ribbon(self): sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: sorted(M._ribbon) [4, 5, 6, 7, 8] - sage: M._test_valid_ribbon(M._ribbon) + sage: M._is_valid_ribbon(M._ribbon) True sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) sage: sorted(M2._ribbon) [4, 7, 8] - sage: M2._test_valid_ribbon(M2._ribbon) + sage: M2._is_valid_ribbon(M2._ribbon) True sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) sage: sorted(M3._ribbon) [1, 2, 3, 4] - sage: M3._test_valid_ribbon(M3._ribbon) + sage: M3._is_valid_ribbon(M3._ribbon) True """ - return list(map(lambda x: self._vertex_to_element(x), self._compute_ribbon())) + return [self._vertex_to_element(x) for x in self._compute_ribbon()] def _compute_ribbon(self): r""" @@ -181,6 +210,7 @@ def _compute_ribbon(self): # First check path counts between ends and deg3 vertex # Then check if more than one max elmt on way to degree 3 vertex. + # Then check if the edge going to a max element is down fron the degree 3 vertex # Arbitrarily choose between ones with just 1 ends = max_elmt_graph.vertices(degree=1) @@ -192,30 +222,33 @@ def _compute_ribbon(self): anchoredEnd = end break - if not anchoredEnd is None: - path = H.shortest_path(deg3, anchoredEnd) + if anchoredEnd is not None: ends.remove(anchoredEnd) - return G.shortest_path(ends[0], ends[1]) possible_anchors = ends[:] for end in ends: path = G.shortest_path(end, deg3) - if not sum(map(lambda z: z in max_elmts, path)) == 1: + if sum(map(lambda z: z in max_elmts, path)) != 1: possible_anchors.remove(end) + for p in possible_anchors: + path = G.shortest_path(p, deg3) + if max_elmt_graph.has_edge(path[-2], path[-1]): + possible_anchors.remove(p) + anchoredEnd = possible_anchors[0] ends.remove(anchoredEnd) return G.shortest_path(ends[0], ends[1]) def get_ribbon(self): r""" - Returns the ribbon of the mobile poset + Return the ribbon of the mobile poset """ return self._ribbon def get_anchor(self): r""" - Returns the anchor of the mobile poset + Return the anchor of the mobile poset """ return self._anchor \ No newline at end of file diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 996a6ebd2e5..cd3d5c7612e 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -90,7 +90,7 @@ from sage.combinat.permutation import Permutations, Permutation, to_standard from sage.combinat.posets.posets import Poset, FinitePoset, FinitePosets_n from sage.combinat.posets.d_complete import DCompletePoset -from sage.combinat.posets.mobile import MobilePoset +from sage.combinat.posets.mobile import MobilePoset as Mobile from sage.combinat.posets.lattices import (LatticePoset, MeetSemilattice, JoinSemilattice, FiniteLatticePoset) from sage.categories.finite_posets import FinitePosets @@ -1781,7 +1781,7 @@ def RibbonPoset(n, descents): sage: sorted(R.cover_relations()) [[0, 1], [2, 1], [3, 2], [3, 4]] """ - return MobilePoset(DiGraph([list(range(n)), [(i+1, i) if i in descents else (i, i+1) for i in range(n-1) ]])) + return Mobile(DiGraph([list(range(n)), [(i+1, i) if i in descents else (i, i+1) for i in range(n-1) ]])) @staticmethod def MobilePoset(ribbon, hangers, anchor=None): @@ -1837,7 +1837,7 @@ def MobilePoset(ribbon, hangers, anchor=None): cover_relations.append(((r, i, cr[0]), (r, i, cr[1]))) cover_relations.append(((r,i,h.top()), r)) - return MobilePoset(DiGraph([elements, cover_relations])) + return Mobile(DiGraph([elements, cover_relations])) ## RANDOM LATTICES From 395573e6ea11c27315f55483899acc87f87fce60 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sat, 22 Aug 2020 12:45:49 -0400 Subject: [PATCH 307/379] changed back to degree --- src/sage/graphs/generic_graph.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 290530e444f..9d993e28ede 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10417,7 +10417,7 @@ def get_vertices(self, verts=None): return {v: self._assoc.get(v, None) for v in verts} - def vertex_iterator(self, vertices=None, vertex_property=None): + def vertex_iterator(self, vertices=None, degree=None): """ Return an iterator over the given vertices. @@ -10430,9 +10430,8 @@ def vertex_iterator(self, vertices=None, vertex_property=None): - ``vertices`` -- iterated vertices are these intersected with the vertices of the (di)graph - - ``vertex_property`` -- function (default: ``None``); a function - that inputs a vertex and outputs a boolean value, i.e., a vertex - ``v`` is kept if ``vertex_property(v) == True`` + - ``degree`` -- a nonnegative integer (default: ``None``); a vertex ``v`` + is kept if ``degree(v) == degree`` EXAMPLES:: @@ -10459,8 +10458,7 @@ def vertex_iterator(self, vertices=None, vertex_property=None): :: sage: H = graphs.PathGraph(5) - sage: degree_one = lambda v: H.degree(v) == 1 - sage: for v in H.vertex_iterator(vertex_property=degree_one): + sage: for v in H.vertex_iterator(degree=1): ....: print(v) 0 4 @@ -10474,9 +10472,9 @@ def vertex_iterator(self, vertices=None, vertex_property=None): sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop """ - if vertex_property is not None: + if degree is not None: for v in self._backend.iterator_verts(vertices): - if vertex_property(v): + if self._backend.degree(v, self._directed) == degree: yield v else: yield from self._backend.iterator_verts(vertices) @@ -10555,7 +10553,7 @@ def neighbor_iterator(self, vertex, closed=False): for u in self._backend.iterator_nbrs(vertex): yield u - def vertices(self, sort=True, key=None, vertex_property=None): + def vertices(self, sort=True, key=None, degree=None): r""" Return a list of the vertices. @@ -10568,9 +10566,8 @@ def vertices(self, sort=True, key=None, vertex_property=None): vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have ``sort=True``) - - ``vertex_property`` -- function (default: ``None``); a function that inputs - a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if - ``vertex_property(v) == True`` + - ``degree`` -- a nonnegative integer (default: ``None``); a vertex ``v`` + is kept if ``degree(v) == degree`` OUTPUT: @@ -10649,8 +10646,8 @@ def vertices(self, sort=True, key=None, vertex_property=None): if (not sort) and key: raise ValueError('sort keyword is False, yet a key function is given') if sort: - return sorted(self.vertex_iterator(vertex_property=vertex_property), key=key) - return list(self.vertex_iterator(vertex_property=vertex_property)) + return sorted(self.vertex_iterator(degree=degree), key=key) + return list(self.vertex_iterator(degree=degree)) def neighbors(self, vertex, closed=False): """ From b0427ad8a6d274c04285d2305279e7e1eb038dd0 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Sat, 22 Aug 2020 12:58:08 -0400 Subject: [PATCH 308/379] nicer code --- src/sage/graphs/generic_graph.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 9d993e28ede..86dc59f59e2 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10472,10 +10472,9 @@ def vertex_iterator(self, vertices=None, degree=None): sage: timeit V = list(P.vertex_iterator()) # not tested 100000 loops, best of 3: 5.74 [micro]s per loop """ - if degree is not None: - for v in self._backend.iterator_verts(vertices): - if self._backend.degree(v, self._directed) == degree: - yield v + if degree: + yield from [v for v, d in self.degree_iterator(labels=True) if d == degree] + else: yield from self._backend.iterator_verts(vertices) From 0c76026f804ae7594db16dacd0ce318691b361e4 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 22 Aug 2020 13:18:23 -0700 Subject: [PATCH 309/379] Add optional package: nodejs --- build/pkgs/nodejs/SPKG.rst | 24 ++++++++++++++ build/pkgs/nodejs/dependencies | 5 +++ build/pkgs/nodejs/package-version.txt | 1 + build/pkgs/nodejs/spkg-install | 45 +++++++++++++++++++++++++++ build/pkgs/nodejs/type | 1 + 5 files changed, 76 insertions(+) create mode 100644 build/pkgs/nodejs/SPKG.rst create mode 100644 build/pkgs/nodejs/dependencies create mode 100644 build/pkgs/nodejs/package-version.txt create mode 100755 build/pkgs/nodejs/spkg-install create mode 100644 build/pkgs/nodejs/type diff --git a/build/pkgs/nodejs/SPKG.rst b/build/pkgs/nodejs/SPKG.rst new file mode 100644 index 00000000000..3a18fc9d838 --- /dev/null +++ b/build/pkgs/nodejs/SPKG.rst @@ -0,0 +1,24 @@ +Node.js +======= + +Description +----------- + +Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. + +It is installed into an isolated nodeenv. + +License +------- + +MIT License + +Upstream Contact +---------------- + +Home page: https://nodejs.org/ + +Dependencies +------------ + +- nodeenv diff --git a/build/pkgs/nodejs/dependencies b/build/pkgs/nodejs/dependencies new file mode 100644 index 00000000000..943a10ee5f5 --- /dev/null +++ b/build/pkgs/nodejs/dependencies @@ -0,0 +1,5 @@ +nodeenv + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/nodejs/package-version.txt b/build/pkgs/nodejs/package-version.txt new file mode 100644 index 00000000000..9cd25a1fec8 --- /dev/null +++ b/build/pkgs/nodejs/package-version.txt @@ -0,0 +1 @@ +12.18.3 diff --git a/build/pkgs/nodejs/spkg-install b/build/pkgs/nodejs/spkg-install new file mode 100755 index 00000000000..b6b27759ab0 --- /dev/null +++ b/build/pkgs/nodejs/spkg-install @@ -0,0 +1,45 @@ +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +nodejs_ver=`grep -oP "\d+\.\d+\.\d+" package-version.txt` + +if [ $? -ne 0 ]; then + echo "Error determining which nodejs version to install ... exiting" + exit 1 +fi + +nodeenv_dir="$SAGE_LOCAL/share/nodejs/$nodejs_ver" +nodeenv_activate="$nodeenv_dir/bin/activate" + +echo "Will use/install nodejs in nodeenv located at $nodeenv_dir ..." + +if [ ! -f "$nodeenv_activate" ]; then + # The nodeenv may not exist, or it may exist but installing nodejs into + # it previously failed, so --force to cover both cases. + nodeenv --force --verbose --node="$nodejs_ver" "$nodeenv_dir" + + if [ $? -ne 0 ]; then + echo "Error installing nodejs ... exiting" + exit 1 + fi +fi + +. "$nodeenv_activate" +active_ver=`node --version | grep -oP "\d+\.\d+\.\d+"` + +if [ $? -ne 0 ]; then + echo "Error determining which nodejs version is active ... exiting" + deactivate_node + exit 1 +fi + +deactivate_node + +if [ ! "$nodejs_ver" = "$active_ver" ]; then + echo "Wrong version of nodejs was activated ... exiting" + echo "Expected $nodejs_ver but found $active_ver" + exit 1 +fi diff --git a/build/pkgs/nodejs/type b/build/pkgs/nodejs/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/nodejs/type @@ -0,0 +1 @@ +optional From 7d2afea7547fa1324893b8b5b3f47823d5c35e42 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 22 Aug 2020 13:59:24 -0700 Subject: [PATCH 310/379] When nodejs installed, activate its nodeenv in sage-env --- build/pkgs/nodejs/spkg-install | 8 ++++++++ src/bin/sage-env | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/build/pkgs/nodejs/spkg-install b/build/pkgs/nodejs/spkg-install index b6b27759ab0..e2671b5ac68 100755 --- a/build/pkgs/nodejs/spkg-install +++ b/build/pkgs/nodejs/spkg-install @@ -28,6 +28,12 @@ if [ ! -f "$nodeenv_activate" ]; then fi . "$nodeenv_activate" + +if [ $? -ne 0 ]; then + echo "Error activating nodeenv containing nodejs ... exiting" + exit 1 +fi + active_ver=`node --version | grep -oP "\d+\.\d+\.\d+"` if [ $? -ne 0 ]; then @@ -43,3 +49,5 @@ if [ ! "$nodejs_ver" = "$active_ver" ]; then echo "Expected $nodejs_ver but found $active_ver" exit 1 fi + +ln -sf "$nodeenv_activate" "$SAGE_LOCAL/share/nodejs/activate" diff --git a/src/bin/sage-env b/src/bin/sage-env index d8c7912112c..8c8026c9596 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -669,3 +669,15 @@ fi # tell it where to find it. See Trac Ticket #15091 export TERMINFO="$SAGE_LOCAL/share/terminfo" + +# If nodejs is installed, activate the nodeenv containing it. + +nodeenv_activate=`resolvelinks "$SAGE_LOCAL/share/nodejs/activate"` + +if [ -f "$nodeenv_activate" ]; then + . "$nodeenv_activate" + + if [ $? -ne 0 ]; then + echo >&2 "Warning: failed to activate the nodeenv containing nodejs" + fi +fi From d2d8c4054e946a93fa8bf3bbfe8865eeb2b6f2fa Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 22 Aug 2020 14:11:34 -0700 Subject: [PATCH 311/379] Add optional package: jupyterlab_widgets --- build/pkgs/jupyterlab_widgets/SPKG.rst | 23 +++++++++++++++++++ build/pkgs/jupyterlab_widgets/dependencies | 5 ++++ .../jupyterlab_widgets/package-version.txt | 1 + build/pkgs/jupyterlab_widgets/spkg-install | 19 +++++++++++++++ build/pkgs/jupyterlab_widgets/type | 1 + 5 files changed, 49 insertions(+) create mode 100644 build/pkgs/jupyterlab_widgets/SPKG.rst create mode 100644 build/pkgs/jupyterlab_widgets/dependencies create mode 100644 build/pkgs/jupyterlab_widgets/package-version.txt create mode 100755 build/pkgs/jupyterlab_widgets/spkg-install create mode 100644 build/pkgs/jupyterlab_widgets/type diff --git a/build/pkgs/jupyterlab_widgets/SPKG.rst b/build/pkgs/jupyterlab_widgets/SPKG.rst new file mode 100644 index 00000000000..89539987bec --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/SPKG.rst @@ -0,0 +1,23 @@ +Jupyter Widgets JupyterLab Extension +==================================== + +Description +----------- + +A JupyterLab extension for Jupyter/IPython widgets. + +License +------- + +BSD License + +Upstream Contact +---------------- + +Home page: https://github.com/jupyter-widgets/ipywidgets + +Dependencies +------------ + +- jupyterlab +- nodejs diff --git a/build/pkgs/jupyterlab_widgets/dependencies b/build/pkgs/jupyterlab_widgets/dependencies new file mode 100644 index 00000000000..2a670ff87ef --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/dependencies @@ -0,0 +1,5 @@ +jupyterlab nodejs + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/jupyterlab_widgets/package-version.txt b/build/pkgs/jupyterlab_widgets/package-version.txt new file mode 100644 index 00000000000..cd5ac039d67 --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/package-version.txt @@ -0,0 +1 @@ +2.0 diff --git a/build/pkgs/jupyterlab_widgets/spkg-install b/build/pkgs/jupyterlab_widgets/spkg-install new file mode 100755 index 00000000000..9376882aaa0 --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/spkg-install @@ -0,0 +1,19 @@ +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +widgets_ver=`cat package-version.txt` + +if [ $? -ne 0 ]; then + echo "Error determining which jupyter-widgets version to install ... exiting" + exit 1 +fi + +jupyter labextension install "@jupyter-widgets/jupyterlab-manager@$widgets_ver" + +if [ $? -ne 0 ]; then + echo "Error installing jupyter-widgets extension into jupyterlab ... exiting" + exit 1 +fi diff --git a/build/pkgs/jupyterlab_widgets/type b/build/pkgs/jupyterlab_widgets/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/jupyterlab_widgets/type @@ -0,0 +1 @@ +optional From c0b02c494a7f5cb66f8d74cca63190fc390b7bdb Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 22 Aug 2020 16:16:17 -0700 Subject: [PATCH 312/379] Fix sage-sh/-buildsh prompt getting wiped out by nodeenv --- src/bin/sage-env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/sage-env b/src/bin/sage-env index 8c8026c9596..19ebad3ced6 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -675,7 +675,7 @@ export TERMINFO="$SAGE_LOCAL/share/terminfo" nodeenv_activate=`resolvelinks "$SAGE_LOCAL/share/nodejs/activate"` if [ -f "$nodeenv_activate" ]; then - . "$nodeenv_activate" + NODE_VIRTUAL_ENV_DISABLE_PROMPT=1 . "$nodeenv_activate" if [ $? -ne 0 ]; then echo >&2 "Warning: failed to activate the nodeenv containing nodejs" From b7010f68662a73cb26d16216b430751173fd6ae3 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 22 Aug 2020 16:17:18 -0700 Subject: [PATCH 313/379] Use https url in doc for jupyterlab package --- build/pkgs/jupyterlab/SPKG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/jupyterlab/SPKG.rst b/build/pkgs/jupyterlab/SPKG.rst index 3f1f1cb6fed..dd1501cbf42 100644 --- a/build/pkgs/jupyterlab/SPKG.rst +++ b/build/pkgs/jupyterlab/SPKG.rst @@ -15,7 +15,7 @@ BSD License Upstream Contact ---------------- -Home page: http://jupyter.org/ +Home page: https://jupyter.org/ Dependencies ------------ From c25f1e3e607e505b6d26fe19214c0184dc00ec7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Labb=C3=A9?= Date: Sun, 9 Aug 2020 12:54:35 +0200 Subject: [PATCH 314/379] Improvements in tikz method Updated tutorial Added tikz method to thematic tutorial --- .../geometry/polytope_tikz.rst | 12 +- .../geometry/visualization.rst | 18 +++ src/sage/geometry/polyhedron/base.py | 95 +++++++++++++++ src/sage/geometry/polyhedron/plot.py | 109 ++++++++++++++---- 4 files changed, 203 insertions(+), 31 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index f5d45485680..9fb6be2700e 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -30,7 +30,7 @@ To put an image of a 3D-polytope in LaTeX using TikZ and Sage, simply follow the - Visualize the polytope P using the command ``P.show(aspect_ratio=1)`` - This will open an interactive view in your default browser, where you can rotate the polytope. - Once the desired view angle is found, click on the information icon in the lower right-hand corner and select *Get Viewpoint*. This will copy a string of the form '[x,y,z],angle' to your local clipboard. -- Go back to Sage and type ``Img = P.projection().tikz([x,y,z],angle)``. You can paste the string here to save some typing. +- Go back to Sage and type ``Img = P.tikz([x,y,z],angle)``. You can paste the string here to save some typing. - *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope Then, you can either copy-paste it to your article by typing ``Img`` in Sage or save it to a file, by doing @@ -80,7 +80,7 @@ When you found a good angle, follow the above procedure to obtain the values :: - Img = P.projection().tikz([674,108,-731],112) + Img = P.tikz([674,108,-731],112) .. end of output @@ -88,7 +88,7 @@ Or you may want to customize using the command :: - Img = P.projection().tikz([674,108,-731],112,scale=2, edge_color='orange',facet_color='red',vertex_color='blue',opacity=0.4) + Img = P.tikz([674,108,-731],112,scale=2, edge_color='orange',facet_color='red',vertex_color='blue',opacity=0.4) .. end of output @@ -134,7 +134,7 @@ some possibilities. .. CODE-BLOCK:: latex - \sagestr{(polytopes.permutahedron(4)).projection().tikz([4,5,6],45,scale=0.75, facet_color='red',vertex_color='yellow',opacity=0.3)} + \sagestr{(polytopes.permutahedron(4)).tikz([4,5,6],45,scale=0.75, facet_color='red',vertex_color='yellow',opacity=0.3)} .. end of output @@ -142,8 +142,8 @@ some possibilities. .. CODE-BLOCK:: latex - \newcommand{\polytopeimg}[4]{\sagestr{(#1).projection().tikz(#2,#3,#4)}} - \newcommand{\polytopeimgopt}[9]{\sagestr{(#1).projection().tikz(#2,#3,#4,#5,#6,#7,#8,#9)}} + \newcommand{\polytopeimg}[4]{\sagestr{(#1).tikz(#2,#3,#4)}} + \newcommand{\polytopeimgopt}[9]{\sagestr{(#1).tikz(#2,#3,#4,#5,#6,#7,#8,#9)}} .. end of output diff --git a/src/doc/en/thematic_tutorials/geometry/visualization.rst b/src/doc/en/thematic_tutorials/geometry/visualization.rst index acc89ce64f0..3dc4e278c06 100644 --- a/src/doc/en/thematic_tutorials/geometry/visualization.rst +++ b/src/doc/en/thematic_tutorials/geometry/visualization.rst @@ -82,3 +82,21 @@ We can see it from a different perspective: Graphics3d Object .. end of output + +:code:`tikz` +================================================== + +This method returns a tikz picture of the polytope (must be 2 or +3-dimensional). For more detail see the tutorial :ref:`polytikz`. + +:: + + sage: c = polytopes.cube() + sage: c.tikz().splitlines()[:5] + ['\\begin{tikzpicture}%', + '\t[x={(1.000000cm, 0.000000cm)},', + '\ty={(-0.000000cm, 1.000000cm)},', + '\tz={(0.000000cm, -0.000000cm)},', + '\tscale=1.000000,'] + +.. end of output diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 57306813cf8..1cc299641aa 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1080,6 +1080,101 @@ def show(self, **kwds): """ self.plot(**kwds).show() + def tikz(self, view=[0, 0, 1], angle=0, scale=1, + edge_color='blue!95!black', facet_color='blue!95!black', + opacity=0.8, vertex_color='green', axis=False): + r""" + Return a string ``tikz_pic`` consisting of a tikz picture of ``self`` + according to a projection ``view`` and an angle ``angle`` + obtained via the threejs viewer. + + INPUT: + + - ``view`` - list (default: [0,0,1]) representing the rotation axis (see note below). + - ``angle`` - integer (default: 0) angle of rotation in degree from 0 to 360 (see note + below). + - ``scale`` - integer (default: 1) specifying the scaling of the tikz picture. + - ``edge_color`` - string (default: 'blue!95!black') representing colors which tikz + recognize. + - ``facet_color`` - string (default: 'blue!95!black') representing colors which tikz + recognize. + - ``vertex_color`` - string (default: 'green') representing colors which tikz + recognize. + - ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of + the front facets. + - ``axis`` - Boolean (default: False) draw the axes at the origin or not. + + OUTPUT: + + - LatexExpr -- containing the TikZ picture. + + .. NOTE:: + + This is a wrapper of a method of the projection object + `self.projection()`. See :meth:`~sage.geometry.polyhedron.plot.Projection.tikz` + for more detail. + + The inputs ``view`` and ``angle`` can be obtained by visualizing it + using ``.show(aspect_ratio=1)``. This will open an interactive view + in your default browser, where you can rotate the polytope. Once + the desired view angle is found, click on the information icon in + the lower right-hand corner and select *Get Viewpoint*. This will + copy a string of the form '[x,y,z],angle' to your local clipboard. + Go back to Sage and type ``Img = P.tikz([x,y,z],angle)``. + + The inputs ``view`` and ``angle`` can also be obtained from the + viewer Jmol:: + + 1) Right click on the image + 2) Select ``Console`` + 3) Select the tab ``State`` + 4) Scroll to the line ``moveto`` + + It reads something like:: + + moveto 0.0 {x y z angle} Scale + + The ``view`` is then [x,y,z] and ``angle`` is angle. + The following number is the scale. + + Jmol performs a rotation of ``angle`` degrees along the + vector [x,y,z] and show the result from the z-axis. + + + EXAMPLES:: + + sage: co = polytopes.cuboctahedron() + sage: Img = co.tikz([0,0,1], 0) + sage: print('\n'.join(Img.splitlines()[:9])) + \begin{tikzpicture}% + [x={(1.000000cm, 0.000000cm)}, + y={(0.000000cm, 1.000000cm)}, + z={(0.000000cm, 0.000000cm)}, + scale=1.000000, + back/.style={loosely dotted, thin}, + edge/.style={color=blue!95!black, thick}, + facet/.style={fill=blue!95!black,fill opacity=0.800000}, + vertex/.style={inner sep=1pt,circle,draw=green!25!black,fill=green!75!black,thick}] + sage: print('\n'.join(Img.splitlines()[12:21])) + %% with the command: ._tikz_3d_in_3d and parameters: + %% view = [0, 0, 1] + %% angle = 0 + %% scale = 1 + %% edge_color = blue!95!black + %% facet_color = blue!95!black + %% opacity = 0.8 + %% vertex_color = green + %% axis = False + sage: print('\n'.join(Img.splitlines()[22:26])) + %% Coordinate of the vertices: + %% + \coordinate (-1.00000, -1.00000, 0.00000) at (-1.00000, -1.00000, 0.00000); + \coordinate (-1.00000, 0.00000, -1.00000) at (-1.00000, 0.00000, -1.00000); + """ + return self.projection().tikz(view, angle, scale, + edge_color, facet_color, + opacity, vertex_color, axis) + def _repr_(self): """ Return a description of the polyhedron. diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 6b167bfdf6c..a087f96a13e 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1097,9 +1097,9 @@ def render_3d(self, point_opts={}, line_opts={}, polygon_opts={}): lplt = self.render_wireframe_3d(**line_opts) if isinstance(polygon_opts, dict): pgplt = self.render_solid_3d(**polygon_opts) - return sum(_ for _ in [pplt, lplt, pgplt] if _ != None) + return sum(_ for _ in [pplt, lplt, pgplt] if _ is not None) - def tikz(self, view=[0, 0, 1], angle=0, scale=2, + def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', opacity=0.8, vertex_color='green', axis=False): r""" @@ -1112,7 +1112,7 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=2, - ``view`` - list (default: [0,0,1]) representing the rotation axis (see note below). - ``angle`` - integer (default: 0) angle of rotation in degree from 0 to 360 (see note below). - - ``scale`` - integer (default: 2) specifying the scaling of the tikz picture. + - ``scale`` - integer (default: 1) specifying the scaling of the tikz picture. - ``edge_color`` - string (default: 'blue!95!black') representing colors which tikz recognize. - ``facet_color`` - string (default: 'blue!95!black') representing colors which tikz @@ -1129,7 +1129,15 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=2, .. NOTE:: - The inputs ``view`` and ``angle`` can be obtained from the + The inputs ``view`` and ``angle`` can be obtained by visualizing it + using ``.show(aspect_ratio=1)``. This will open an interactive view + in your default browser, where you can rotate the polytope. Once + the desired view angle is found, click on the information icon in + the lower right-hand corner and select *Get Viewpoint*. This will + copy a string of the form '[x,y,z],angle' to your local clipboard. + Go back to Sage and type ``Img = P.projection().tikz([x,y,z],angle)``. + + The inputs ``view`` and ``angle`` can also be obtained from the viewer Jmol:: 1) Right click on the image @@ -1299,12 +1307,24 @@ def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color,opacity) tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color - tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n%%\n%%\n' % vertex_color + tikz_pic += 'fill=%s!75!black,thick}]\n%%\n%%\n' % vertex_color + + # Gives the reproduction information + from sage.env import SAGE_VERSION + tikz_pic += "%% This TikZ-picture was produce with Sagemath version {}\n".format(SAGE_VERSION) + tikz_pic += "%% with the command: ._tikz_2d and parameters:\n" + tikz_pic += "%% scale = {}\n".format(scale) + tikz_pic += "%% edge_color = {}\n".format(edge_color) + tikz_pic += "%% facet_color = {}\n".format(facet_color) + tikz_pic += "%% opacity = {}\n".format(opacity) + tikz_pic += "%% vertex_color = {}\n".format(vertex_color) + tikz_pic += "%% axis = {}\n\n".format(axis) # Draws the axes if True if axis: + tikz_pic += '%% Drawing the axes\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n' - tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n' + tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n\n' # Create the coordinate of the vertices: tikz_pic += '%% Coordinate of the vertices:\n%%\n' @@ -1378,11 +1398,16 @@ def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color, sage: p = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]) sage: proj = p.projection() sage: Img = proj.tikz([1,1,1],130,axis=True) - sage: print('\n'.join(Img.splitlines()[21:25])) - %% Drawing the interior - %% - \fill[facet] (1.00000, 0.00000, 0.00000) -- (0.00000, 0.00000, 1.00000) -- (0.00000, 1.00000, 0.00000) -- cycle {}; - %% + sage: print('\n'.join(Img.splitlines()[12:21])) + %% with the command: ._tikz_2d_in_3d and parameters: + %% view = [1, 1, 1] + %% angle = 130 + %% scale = 1 + %% edge_color = blue!95!black + %% facet_color = blue!95!black + %% opacity = 0.8 + %% vertex_color = green + %% axis = True .. NOTE:: @@ -1427,10 +1452,24 @@ def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color, tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color,opacity) tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color - tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n%%\n%%\n' % vertex_color + tikz_pic += 'fill=%s!75!black,thick}]\n%%\n%%\n' % vertex_color + + # Gives the reproduction information + from sage.env import SAGE_VERSION + tikz_pic += "%% This TikZ-picture was produce with Sagemath version {}\n".format(SAGE_VERSION) + tikz_pic += "%% with the command: ._tikz_2d_in_3d and parameters:\n" + tikz_pic += "%% view = {}\n".format(view) + tikz_pic += "%% angle = {}\n".format(angle) + tikz_pic += "%% scale = {}\n".format(scale) + tikz_pic += "%% edge_color = {}\n".format(edge_color) + tikz_pic += "%% facet_color = {}\n".format(facet_color) + tikz_pic += "%% opacity = {}\n".format(opacity) + tikz_pic += "%% vertex_color = {}\n".format(vertex_color) + tikz_pic += "%% axis = {}\n\n".format(axis) # Draws the axes if True if axis: + tikz_pic += '%% Drawing the axes\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n' @@ -1506,19 +1545,25 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, sage: Associahedron = Polyhedron(vertices=[[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]]).polar() sage: ImageAsso = Associahedron.projection().tikz([-15,-755,-655], 116, scale=1) - sage: print('\n'.join(ImageAsso.splitlines()[29:41])) - %% Drawing edges in the back - %% - \draw[edge,back] (0.00000, 1.00000, -1.00000) -- (0.00000, 0.00000, -1.00000); - \draw[edge,back] (1.00000, -1.00000, 0.00000) -- (0.00000, -1.00000, 0.00000); - \draw[edge,back] (1.00000, 0.00000, -1.00000) -- (0.00000, 0.00000, -1.00000); - \draw[edge,back] (0.00000, 0.00000, -1.00000) -- (-0.50000, -0.50000, -0.50000); - \draw[edge,back] (-1.00000, 1.00000, 0.00000) -- (-1.00000, 0.00000, 0.00000); - \draw[edge,back] (-1.00000, 0.00000, 0.00000) -- (-1.00000, 0.00000, 1.00000); - \draw[edge,back] (-1.00000, 0.00000, 0.00000) -- (-0.50000, -0.50000, -0.50000); - \draw[edge,back] (0.00000, -1.00000, 1.00000) -- (0.00000, -1.00000, 0.00000); - \draw[edge,back] (0.00000, -1.00000, 0.00000) -- (-0.50000, -0.50000, -0.50000); + sage: print('\n'.join(ImageAsso.splitlines()[12:30])) + %% with the command: ._tikz_3d_in_3d and parameters: + %% view = [-15, -755, -655] + %% angle = 116 + %% scale = 1 + %% edge_color = blue!95!black + %% facet_color = blue!95!black + %% opacity = 0.8 + %% vertex_color = green + %% axis = False + + %% Coordinate of the vertices: %% + \coordinate (0.00000, 1.00000, -1.00000) at (0.00000, 1.00000, -1.00000); + \coordinate (1.00000, 1.00000, -1.00000) at (1.00000, 1.00000, -1.00000); + \coordinate (1.00000, 1.00000, 1.00000) at (1.00000, 1.00000, 1.00000); + \coordinate (1.00000, -1.00000, 1.00000) at (1.00000, -1.00000, 1.00000); + \coordinate (1.00000, -1.00000, 0.00000) at (1.00000, -1.00000, 0.00000); + \coordinate (1.00000, 0.00000, -1.00000) at (1.00000, 0.00000, -1.00000); """ view_vector = vector(RDF, view) rot = rotate_arbitrary(view_vector, -(angle/360)*2*pi) @@ -1601,10 +1646,24 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color,opacity) tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color - tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n%%\n%%\n' % vertex_color + tikz_pic += 'fill=%s!75!black,thick}]\n%%\n%%\n' % vertex_color + + # Gives the reproduction information + from sage.env import SAGE_VERSION + tikz_pic += "%% This TikZ-picture was produce with Sagemath version {}\n".format(SAGE_VERSION) + tikz_pic += "%% with the command: ._tikz_3d_in_3d and parameters:\n" + tikz_pic += "%% view = {}\n".format(view) + tikz_pic += "%% angle = {}\n".format(angle) + tikz_pic += "%% scale = {}\n".format(scale) + tikz_pic += "%% edge_color = {}\n".format(edge_color) + tikz_pic += "%% facet_color = {}\n".format(facet_color) + tikz_pic += "%% opacity = {}\n".format(opacity) + tikz_pic += "%% vertex_color = {}\n".format(vertex_color) + tikz_pic += "%% axis = {}\n\n".format(axis) # Draws the axes if True if axis: + tikz_pic += '%% Drawing the axes\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n' From a0049e35e9a6628b193b7143dd7a58b1d0d8b820 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Sun, 23 Aug 2020 18:44:35 +0200 Subject: [PATCH 315/379] #30412 : new upstream tarball that ships configure script --- build/pkgs/gf2x/checksums.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/gf2x/checksums.ini b/build/pkgs/gf2x/checksums.ini index 405b2df3da5..a85eac9189e 100644 --- a/build/pkgs/gf2x/checksums.ini +++ b/build/pkgs/gf2x/checksums.ini @@ -1,5 +1,5 @@ tarball=gf2x-VERSION.tar.gz -sha1=2a9099d6e3c1d0890e55e93dd4ea635fb1a64a42 -md5=18bccf5efd998424da5e99d4575de0ed -cksum=1705713312 +sha1=1b9c7e14031afc5488b9aa27f5501f78c90f00b4 +md5=842f087ce423c279dced26b85b0fd1d0 +cksum=3368093312 upstream_url=https://gitlab.inria.fr/gf2x/gf2x/-/archive/gf2x-VERSION/gf2x-gf2x-VERSION.tar.gz From cd5d292893604193ec03e67a14cfb11f8024da95 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Sun, 23 Aug 2020 18:45:39 +0200 Subject: [PATCH 316/379] #30412 : do not run autotools in spkg-install --- build/pkgs/gf2x/spkg-install.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/pkgs/gf2x/spkg-install.in b/build/pkgs/gf2x/spkg-install.in index 2abee3cd6d6..17a0ad7b6cb 100644 --- a/build/pkgs/gf2x/spkg-install.in +++ b/build/pkgs/gf2x/spkg-install.in @@ -7,10 +7,6 @@ cd src # Use newer version of config.guess and config.sub (see Trac #19727) cp "$SAGE_ROOT"/config/config.* config -# Run libtool/autotools -libtoolize -autoreconf -ivf - if [ "$SAGE_DEBUG" = "yes" ]; then echo "Building a debug version of gf2x." export CFLAGS="-O0 -g $CFLAGS" From d3772f64daaaac38db5ef84d1f2bd3eef2ba2209 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Sun, 23 Aug 2020 23:20:26 +0200 Subject: [PATCH 317/379] #30412 : link to updated upstream tarball location --- build/pkgs/gf2x/checksums.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/gf2x/checksums.ini b/build/pkgs/gf2x/checksums.ini index a85eac9189e..10a93c2c1bc 100644 --- a/build/pkgs/gf2x/checksums.ini +++ b/build/pkgs/gf2x/checksums.ini @@ -2,4 +2,4 @@ tarball=gf2x-VERSION.tar.gz sha1=1b9c7e14031afc5488b9aa27f5501f78c90f00b4 md5=842f087ce423c279dced26b85b0fd1d0 cksum=3368093312 -upstream_url=https://gitlab.inria.fr/gf2x/gf2x/-/archive/gf2x-VERSION/gf2x-gf2x-VERSION.tar.gz +upstream_url=https://gitlab.inria.fr/gf2x/gf2x/uploads/c46b1047ba841c20d1225ae73ad6e4cd/gf2x-1.3.0.tar.gz From b43cc91b38f38795c8d47ef76f71810269cfe779 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 23 Aug 2020 23:11:30 +0100 Subject: [PATCH 318/379] modernise FAQ --- src/doc/en/faq/faq-general.rst | 11 ++-- src/doc/en/faq/faq-usage.rst | 110 +++++++-------------------------- 2 files changed, 28 insertions(+), 93 deletions(-) diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index f539d0dd56f..53fa60f5c96 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -183,19 +183,22 @@ functionalities are made possible through FOSS projects such as * `ATLAS `_ --- Automatically Tuned Linear Algebra Software. -* `BLAS `_ --- Basic Linear Algebra +* `BLAS `_ --- Basic Linear Algebra Subprograms. +* `ECL `_ --- Embeddable Common-Lisp system * `FLINT `_ --- C library for doing number theory. -* `GAP `_ --- a system for computational +* `GAP `_ --- a system for computational discrete algebra, with particular emphasis on computational group theory. +* `GMP `_ --- GNU Multiple Precision Arithmetic Library. * `Maxima `_ --- system for symbolic and numerical computation. * `mpmath `_ --- a pure-Python library for multiprecision floating-point arithmetic. -* `NumPy `_ --- numerical linear algebra and +* `NumPy and SciPy `_ --- numerical linear algebra and other numerical computing capabilities for Python. +* `OpenBLAS `_ --- an optimized BLAS library. * `Pari/GP `_ --- a computer algebra system for fast computations in number theory. * `Pynac `_ --- a modified version of GiNaC @@ -306,7 +309,7 @@ How can I download the Sage documentation to read it offline? To download the Sage standard documentation in HTML or PDF formats, visit the -`Help and Support `_ +`Help and Support `_ page on the Sage website. Each release of Sage comes with the full documentation that makes up the Sage standard documentation. If you have downloaded a binary Sage release, the HTML version of the diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 0c5efde1b95..5da1732f538 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -28,7 +28,7 @@ The **source code** of Sage is also available for you to download and use. Go to http://www.sagemath.org/download-source.html to download the tar archive for any release of Sage. -The Sage notebook runs within a web browser. To start the notebook, +The Sage Jupyter notebook runs within a web browser. To start the notebook, issue the following command in a terminal, if ``sage`` is in your ``PATH`` .. CODE-BLOCK:: shell-session @@ -74,7 +74,19 @@ Sage. The command will enable 8 threads for parts of the build that support parallelism. Change the number 8 as appropriate to suit the number of -cores on your system. +cores on your system. Some Sage installations may have OpenMP-enabled BLAS +(and other) libraries. The amount of OpenMP parallelism is controlled by +the environment variable OMP_NUM_THREADS; however, it is known to not +play well with Python parallelism, and you might want to + +.. CODE-BLOCK:: shell-session + + $ export OMP_NUM_THREADS=1 + +in case of crashes or hangs. + + +More details may be found in `Installation Manual `_. How to get Sage's Python to recognize my system's Tcl/Tk install? @@ -164,36 +176,6 @@ Can I use SageMath with Python 3.x? Since release 9.0 from January 2020, SageMath is running on top of Python 3. -I'm seeing an error about "Permission denied" on a file called "sage-flags.txt". -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -When sage is built from source, it keeps track of what special -instructions your CPU supports (such as SSE2) and records these. This -is so that if you try running the code on a different machine, which -does not support these extra instructions, you get a sensible error -message instead of a segfault or illegal instruction. Since this -should be stored with Sage itself (as opposed to a user's ``.sage`` -directory), it has to be created by someone with the appropriate -permissions. So if you are seeing something like this - -.. CODE-BLOCK:: pytb - - Traceback (most recent call last): - File "/usr/local/sage-4.0.2/local/bin/sage-location", line 174, in - t, R = install_moved() - File "/usr/local/sage-4.0.2/local/bin/sage-location", line 18, in install_moved - write_flags_file() - File "/usr/local/sage-4.0.2/local/bin/sage-location", line 82, in write_flags_file - open(flags_file,'w').write(get_flags_info()) - IOError: [Errno 13] Permission denied: - '/usr/local/sage-4.0.2/local/lib/sage-flags.txt' - -it probably means that you compiled/installed Sage as one user, but -have not run it to let it generate the ``sage-flags.txt`` file. Just -run Sage one time as whatever user installed it and this problem -should go away. This would also be easy to fix by having Sage run once -as part of the install process; see :trac:`6375` for this fix. - I downloaded a Sage binary and it crashes on startup with "Illegal instruction". What can I do? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -214,16 +196,16 @@ build Sage in such a way that MPIR and ATLAS work on all hardware. This will eventually get fixed. Any help is appreciated. -I used Debian/Ubuntu to install Sage 3.0.5 and that version is giving lots of errors. What can I do? +I used XXX to install Sage X.Y and that version is giving lots of errors. What can I do? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -The version of Sage, i.e. Sage version 3.0.5, that is available -through ``apt-get`` in Debian and Ubuntu is very old. No one has yet -found time to update the Debian/Ubuntu version of Sage. Any help is +The version of Sage, i.e. Sage version X.Y, that is available on your XXX system +through its package manager, is very old. No one has yet +found time to update the XXX version of Sage. Any help is greatly appreciated. You should download the latest version of Sage from the `download page `_. -If you would like to help with updating the Debian/Ubuntu version of +If you would like to help with updating the XXX version of Sage, please email the `sage-devel `_ mailing list. @@ -259,7 +241,7 @@ by a web search. * `How to Think Like a Computer Scientist `_ by Jeffrey Elkner, Allen B. Downey, and Chris Meyers * `Official Python Tutorial `_ -* `Python `_ home page and the +* `Python `_ home page and the `Python standard documentation `_ @@ -386,20 +368,7 @@ How do I save an object so I don't have to compute it each time I open a workshe """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" The ``save`` and ``load`` commands will save and load an object, -respectively. In the notebook, the ``DATA`` variable is the location -of the data storage area of the worksheet. To save the object -``my_stuff`` in a worksheet, you could do - -.. CODE-BLOCK:: python - - save(my_stuff, DATA + "my_stuff") - -and to reload it, you would just do - -.. CODE-BLOCK:: python - - my_stuff = load(DATA + "my_stuff") - +respectively. Does Sage contain a function similar to Mathematica's ToCharacterCode[]? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -502,27 +471,6 @@ can first restore your graphical session, before you attempt to log into a text based session. -Sage 2.9 and higher fails compiling ATLAS on Linux. How can I fix this? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -The most likely cause is enabled power management. Disabling it should -fix the problem. Depending on your flavor of distribution, this might -either be possible with some nice GUI tool or not. On the command line -do the following as root for each CPU you have: - -.. CODE-BLOCK:: shell-session - - $ /usr/bin/cpufreq-selector -g performance -c #number CPU - -On Ubuntu, try disabling "Power Manager" via - -.. CODE-BLOCK:: text - - System --> Preferences --> Sessions - -under the "Startup Programs" or using ``cpufreq-set`` via the command -line. - When I start Sage, SELinux complains that "/path/to/libpari-gmp.so.2" requires text-relocation. How can I fix it? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -729,22 +677,6 @@ then ``^`` will work just like in Python. You can later turn on the preparser with ``preparser(True)``. That only works in command line Sage. In a notebook, switch to Python mode. - -When I try to use LaTeX in the notebook, it says it cannot find fullpage.sty. -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -The general---but perhaps not very helpful---answer is that you need -to install ``fullpage.sty`` into a directory searched by TeX. On -Ubuntu (and probably many other Linux distributions), you should -install the ``texlive-latex-extra`` package. If that is not available, -try installing the ``tetex-extra package``. If you are using Mac OS X, -you will have to use whatever TeX distribution you use to get -``fullpage.sty`` (if you use MacTeX, it is likely already -installed). If you are using the VirtualBox image on Windows, you will -need to log into the VirtualBox image and install -``texlive-latex-extra`` there. - - With objects a and b and a function f, I accidentally typed f(a) = b instead of f(a) == b. This returned a TypeError (as expected), but also deleted the object a. Why? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" From 488bba4768c034c741fa7e929168d354a5a86306 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 23 Aug 2020 19:41:17 +0100 Subject: [PATCH 319/379] on macOS set OMP_NUM_THREADS to 1 for docbuild and tests --- build/bin/sage-site | 7 +++++++ src/bin/sage-runtests | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/build/bin/sage-site b/build/bin/sage-site index 44d8e3acff9..ef5762d3cdc 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -157,6 +157,13 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then # cause the build to hang. If stdin is /dev/null, TeX just aborts. shift export LANG=C # to ensure it is possible to scrape out non-EN locale warnings + + # See #30351: bugs in macOS implementations of openblas/libgopm can cause + # docbuild to hang if multiple OpenMP threads are allowed. + if [ `uname` = 'Darwin' ]; then + export OMP_NUM_THREADS=1 + fi + exec sage-python -m sage_setup.docbuild "$@" Date: Sun, 23 Aug 2020 19:01:41 -0700 Subject: [PATCH 320/379] Resolve nodeenv_activate symlink _after_ checking that it exists --- src/bin/sage-env | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bin/sage-env b/src/bin/sage-env index 19ebad3ced6..d6d51ffc5e4 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -672,12 +672,19 @@ export TERMINFO="$SAGE_LOCAL/share/terminfo" # If nodejs is installed, activate the nodeenv containing it. -nodeenv_activate=`resolvelinks "$SAGE_LOCAL/share/nodejs/activate"` +nodeenv_activate="$SAGE_LOCAL/share/nodejs/activate" if [ -f "$nodeenv_activate" ]; then + # symlinked into nodeenv for specific version of nodejs installed + # The activate script needs to be sourced using its actual path. + nodeenv_activate=`resolvelinks "$nodeenv_activate"` + + # Don't let nodeenv wipe out the sage-sh/sage-buildsh prompt. NODE_VIRTUAL_ENV_DISABLE_PROMPT=1 . "$nodeenv_activate" if [ $? -ne 0 ]; then echo >&2 "Warning: failed to activate the nodeenv containing nodejs" fi +elif [ -L "$nodeenv_activate" ]; then + echo >&2 "Warning: the nodeenv activation symlink for nodejs is broken" fi From 29359e1434f5464023c72bfe652118c2fbc1906a Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sun, 23 Aug 2020 19:13:26 -0700 Subject: [PATCH 321/379] Instruct user to install jupyterlab using -i instead of --pip install --- src/bin/sage-notebook | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook index 889341d8b00..7cfe9a43f69 100755 --- a/src/bin/sage-notebook +++ b/src/bin/sage-notebook @@ -54,7 +54,7 @@ class NotebookJupyterlab(object): except ImportError: print("Jupyterlab is not installed (at least not in this Sage installation).") print("You can install it by running") - print(" sage --pip install jupyterlab") + print(" sage -i jupyterlab") raise SystemExit(1) self.print_banner() main(argv) From 89e138fb6b2ad5edcdb645781978d5f2744d0d5c Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 24 Aug 2020 00:22:37 -0400 Subject: [PATCH 322/379] aded back vertex_property --- src/sage/graphs/generic_graph.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 86dc59f59e2..d020fc327fc 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10417,7 +10417,7 @@ def get_vertices(self, verts=None): return {v: self._assoc.get(v, None) for v in verts} - def vertex_iterator(self, vertices=None, degree=None): + def vertex_iterator(self, vertices=None, degree=None, vertex_property=None): """ Return an iterator over the given vertices. @@ -10433,6 +10433,10 @@ def vertex_iterator(self, vertices=None, degree=None): - ``degree`` -- a nonnegative integer (default: ``None``); a vertex ``v`` is kept if ``degree(v) == degree`` + - ``vertex_property`` -- function (default: ``None``); a function + that inputs a vertex and outputs a boolean value, i.e., a vertex + ``v`` is kept if ``vertex_property(v) == True`` + EXAMPLES:: sage: P = graphs.PetersenGraph() @@ -10458,9 +10462,9 @@ def vertex_iterator(self, vertices=None, degree=None): :: sage: H = graphs.PathGraph(5) - sage: for v in H.vertex_iterator(degree=1): + sage: one_mod_3 = lambda l: l % 3 == 1 + sage: for v in H.vertex_iterator(degree=1, vertex_property=one_mod_3): ....: print(v) - 0 4 Note that since the intersection option is available, the @@ -10473,10 +10477,17 @@ def vertex_iterator(self, vertices=None, degree=None): 100000 loops, best of 3: 5.74 [micro]s per loop """ if degree: - yield from [v for v, d in self.degree_iterator(labels=True) if d == degree] + if vertex_property is not None: + yield from [v for v, d in self.degree_iterator(labels=True) if d == degree and vertex_property(v)] + else: + yield from [v for v, d in self.degree_iterator(labels=True) if d == degree] + + elif vertex_property is not None: + yield from [v for v in self._backend.iterator_verts(vertices) if vertex_property(v)] else: - yield from self._backend.iterator_verts(vertices) + for v in self._backend.iterator_verts(vertices): + yield v __iter__ = vertex_iterator @@ -10552,7 +10563,7 @@ def neighbor_iterator(self, vertex, closed=False): for u in self._backend.iterator_nbrs(vertex): yield u - def vertices(self, sort=True, key=None, degree=None): + def vertices(self, sort=True, key=None, degree=None, vertex_property=None): r""" Return a list of the vertices. @@ -10568,6 +10579,10 @@ def vertices(self, sort=True, key=None, degree=None): - ``degree`` -- a nonnegative integer (default: ``None``); a vertex ``v`` is kept if ``degree(v) == degree`` + - ``vertex_property`` -- function (default: ``None``); a function that inputs + a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if + ``vertex_property(v) == True`` + OUTPUT: The list of vertices of the (di)graph. @@ -10645,8 +10660,8 @@ def vertices(self, sort=True, key=None, degree=None): if (not sort) and key: raise ValueError('sort keyword is False, yet a key function is given') if sort: - return sorted(self.vertex_iterator(degree=degree), key=key) - return list(self.vertex_iterator(degree=degree)) + return sorted(self.vertex_iterator(degree=degree, vertex_property=vertex_property), key=key) + return list(self.vertex_iterator(degree=degree, vertex_property=vertex_property)) def neighbors(self, vertex, closed=False): """ From 63d8e8c28b17a4e86d127976062e7a19e6e30e18 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 24 Aug 2020 00:30:50 -0400 Subject: [PATCH 323/379] shortened docstrings below 80 chars --- src/sage/graphs/generic_graph.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index d020fc327fc..0506ef2de48 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10430,8 +10430,8 @@ def vertex_iterator(self, vertices=None, degree=None, vertex_property=None): - ``vertices`` -- iterated vertices are these intersected with the vertices of the (di)graph - - ``degree`` -- a nonnegative integer (default: ``None``); a vertex ``v`` - is kept if ``degree(v) == degree`` + - ``degree`` -- a nonnegative integer (default: ``None``); + a vertex ``v`` is kept if ``degree(v) == degree`` - ``vertex_property`` -- function (default: ``None``); a function that inputs a vertex and outputs a boolean value, i.e., a vertex @@ -10462,8 +10462,8 @@ def vertex_iterator(self, vertices=None, degree=None, vertex_property=None): :: sage: H = graphs.PathGraph(5) - sage: one_mod_3 = lambda l: l % 3 == 1 - sage: for v in H.vertex_iterator(degree=1, vertex_property=one_mod_3): + sage: prop = lambda l: l % 3 == 1 + sage: for v in H.vertex_iterator(degree=1, vertex_property=prop): ....: print(v) 4 @@ -10576,12 +10576,12 @@ def vertices(self, sort=True, key=None, degree=None, vertex_property=None): vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have ``sort=True``) - - ``degree`` -- a nonnegative integer (default: ``None``); a vertex ``v`` - is kept if ``degree(v) == degree`` + - ``degree`` -- a nonnegative integer (default: ``None``); + a vertex ``v`` is kept if ``degree(v) == degree`` - - ``vertex_property`` -- function (default: ``None``); a function that inputs - a vertex and outputs a boolean value, i.e., a vertex ``v`` is kept if - ``vertex_property(v) == True`` + - ``vertex_property`` -- function (default: ``None``); a function + that inputs a vertex and outputs a boolean value, i.e., a vertex + ``v`` is kept if ``vertex_property(v) == True`` OUTPUT: From feae25e67709ac154428667673635bb3fa87b5d5 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 Aug 2020 16:58:39 +0200 Subject: [PATCH 324/379] merge public/29934 --- src/sage/geometry/polyhedron/base.py | 55 +++++++++++++++---------- src/sage/geometry/polyhedron/library.py | 1 + 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 99e385f2fc6..214a88d8324 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4626,28 +4626,6 @@ def product(self, other): sage: polytopes.hypercube(1) * polytopes.hypercube(2) A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices - - Check that the product preserves the backend, where possible:: - - sage: P = polytopes.simplex(backend='cdd') - sage: Q = polytopes.simplex(backend='ppl') - sage: (P*Q).backend() - 'cdd' - sage: (Q*P).backend() - 'ppl' - sage: (P * polytopes.dodecahedron(backend='field')).backend() - 'field' - - Check that double description is set up correctly:: - - sage: P = polytopes.permutahedron(4).base_extend(QQ) - sage: P1 = Polyhedron(rays=[[1,0,0,0],[0,1,1,0]], lines=[[0,1,0,1]]) - sage: Q = P.base_extend(QQ, 'field') - sage: Q1 = P1.base_extend(QQ, 'field') - sage: P * P1 == Q * Q1 - True - sage: P.polar(in_affine_span=True) * P1 == Q.polar(in_affine_span=True) * Q1 - True """ try: new_ring = self.parent()._coerce_base_ring(other) @@ -4685,6 +4663,39 @@ def product(self, other): _mul_ = product + def _test_product(self, tester=None, **options): + """ + Run tests on the method :meth:`.product`. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_product() + """ + from sage.geometry.polyhedron.library import polytopes + if tester is None: + tester = self._tester(**options) + + if self.n_vertices() + self.n_rays() < 40 and self.n_facets() < 40: + # Check that the product preserves the backend, where possible. + P = polytopes.simplex(backend="cdd") + tester.assertEqual((self*P).backend(), self.backend()) + Q = polytopes.simplex(backend="ppl") + tester.assertEqual((self*Q).backend(), self.backend()) + + # And that it changes the backend correctly where necessary. + if AA.has_coerce_map_from(self.base_ring()): + P = polytopes.regular_polygon(5, exact=True) + if RDF.has_coerce_map_from(self.base_ring()): + R = self*polytopes.regular_polygon(5, exact=False) + + if self.base_ring().is_exact(): + # Check that the double description is set up correctly. + self_field = self.base_extend(self.base_ring(), backend='field') + P = polytopes.permutahedron(4, backend='field').base_extend(QQ) + Q = Polyhedron(rays=[[1,0,0,0],[0,1,1,0]], lines=[[0,1,0,1]], backend='field') + (self_field * P)._test_basic_properties(tester) + (self_field * Q)._test_basic_properties(tester) + def join(self, other): """ Return the join of ``self`` and ``other``. diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 015ea8a3928..b0d2a1ebd80 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -1479,6 +1479,7 @@ def icosidodecahedron(self, exact=True, backend=None): sage: id = polytopes.icosidodecahedron(exact=False); id A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices sage: TestSuite(id).run(skip=["_test_is_combinatorially_isomorphic", + ....: "_test_product", ....: "_test_pyramid", ....: "_test_lawrence"]) From a4bf2bc33270f2a644aec4caa89f54796f034dc2 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 Aug 2020 17:15:19 +0200 Subject: [PATCH 325/379] small fixes to make the test method for the product work --- src/sage/geometry/polyhedron/base.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 214a88d8324..860482f31ab 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -525,15 +525,17 @@ def _test_basic_properties(self, tester=None, **options): tester.assertEqual(self.n_vertices() + self.n_rays() + self.n_lines(), self.n_Vrepresentation()) tester.assertEqual(self.n_inequalities() + self.n_equations(), self.n_Hrepresentation()) - tester.assertEqual(self.dim() + self.n_equations(), self.ambient_dim()) + if self.n_vertices(): + # Depending on the backend, this does not hold for the empty polyhedron. + tester.assertEqual(self.dim() + self.n_equations(), self.ambient_dim()) tester.assertTrue(all(len(v[::]) == self.ambient_dim() for v in self.Vrep_generator())) tester.assertTrue(all(len(h[::]) == self.ambient_dim() + 1 for h in self.Hrep_generator())) if self.n_vertices() + self.n_rays() < 40: - tester.assertEqual(self, Polyhedron(vertices=self.vertices(), rays=self.rays(), lines=self.lines())) + tester.assertEqual(self, Polyhedron(vertices=self.vertices(), rays=self.rays(), lines=self.lines(), ambient_dim=self.ambient_dim())) if self.n_inequalities() < 40: - tester.assertEqual(self, Polyhedron(ieqs=self.inequalities(), eqns=self.equations())) + tester.assertEqual(self, Polyhedron(ieqs=self.inequalities(), eqns=self.equations(), ambient_dim=self.ambient_dim())) def base_extend(self, base_ring, backend=None): """ @@ -4647,6 +4649,13 @@ def product(self, other): lines = chain((tuple(l) + other_zero for l in self.line_generator()), (self_zero + tuple(l) for l in other.line_generator())) + if self.n_vertices() == 0 or other.n_vertices() == 0: + # In this case we obtain the empty polyhedron. + # There is not vertex to attach the rays or lines to. + # By our convenction, in this case the polyhedron shall also not have rays or lines. + rays = () + lines = () + ieqs = chain((tuple(i) + other_zero for i in self.inequality_generator()), ((i.b(),) + self_zero + tuple(i.A()) for i in other.inequality_generator())) From ad7dc7d907fd362fc14aed22103f5c038401e814 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Mon, 24 Aug 2020 12:01:16 +0530 Subject: [PATCH 326/379] review commit --- src/sage/graphs/base/boost_graph.pyx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 7b98e82c5d0..3a066478791 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2627,7 +2627,7 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, shortest paths from. By default (``None``), compute shortest paths from all vertices. - - ``order`` -- list (default: ``None``); order of vertices of `g`. + - ``order`` -- list (default: ``None``); order of vertices of `g` - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of @@ -2637,16 +2637,16 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, - ``algorithm`` -- string (default: ``None``); one of the following algorithms: - - ``'Dijkstra'``, ``'Dijkstra_Boost'``: the Dijkstra algorithm implemented - in Boost (works only with positive weights) + - ``'Dijkstra'``, ``'Dijkstra_Boost'`` - the Dijkstra algorithm + implemented in Boost (works only with positive weights) - - ``'Bellman-Ford'``, ``'Bellman-Ford_Boost'``: the Bellman-Ford algorithm - implemented in Boost (works also with negative weights, if there is no - negative cycle) + - ``'Bellman-Ford'``, ``'Bellman-Ford_Boost'`` - the Bellman-Ford + algorithm implemented in Boost (works also with negative weights, + if there is no negative cycle) OUTPUT: - Two possible outputs: + The type of output depends on the input. More precisely - - A pair of dictionaries of list ``(distances, predecessors)``, when ``order is not None``, such that for each vertex ``v`` in ``vertex_list``, @@ -2738,9 +2738,8 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, if order is not None: if len(g) == len(order): - for vertex in g: - if vertex not in order: - raise ValueError("Given ordering is not valid") + if any(v not in order for v in g): + raise ValueError("Given ordering is not valid") else: raise ValueError("Given ordering is not valid") From 0b470c3aec9dafc0ec98c8e2559469bfb5335cde Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 12 Aug 2020 12:14:22 -0700 Subject: [PATCH 327/379] build/pkgs/cvxopt: Update to 1.2.5, add upstream_url --- build/pkgs/cvxopt/checksums.ini | 7 ++++--- build/pkgs/cvxopt/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cvxopt/checksums.ini b/build/pkgs/cvxopt/checksums.ini index 5e7a6fe107f..c630252eeda 100644 --- a/build/pkgs/cvxopt/checksums.ini +++ b/build/pkgs/cvxopt/checksums.ini @@ -1,4 +1,5 @@ tarball=cvxopt-VERSION.tar.gz -sha1=b16cf3b1c941d763ea0451e947e5661d38ee0473 -md5=aac3db3fc3cf11d9ad2e42d89e94ca5a -cksum=3018809057 +sha1=cc1f19aafa8eb5b135861f43d5d9e18b1304cd11 +md5=45044c5ac2a8f22d4f7572b5459e84dd +cksum=962955599 +upstream_url=https://pypi.io/packages/source/c/cvxopt/cvxopt-VERSION.tar.gz diff --git a/build/pkgs/cvxopt/package-version.txt b/build/pkgs/cvxopt/package-version.txt index 0495c4a88ca..c813fe116c9 100644 --- a/build/pkgs/cvxopt/package-version.txt +++ b/build/pkgs/cvxopt/package-version.txt @@ -1 +1 @@ -1.2.3 +1.2.5 From d83100da9c764dd08a4124e40c10c1687825e4b1 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 24 Aug 2020 16:43:56 +0900 Subject: [PATCH 328/379] Fix plot() to work for vertical lines --- .../polynomial/multi_polynomial_ideal.py | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index a9b5fbabf99..969fbbb41e5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- r""" -Ideals in multivariate polynomial rings. +Ideals in multivariate polynomial rings Sage has a powerful system to compute with multivariate polynomial rings. Most algorithms dealing with these ideals are centered on the @@ -9,21 +8,6 @@ open-source system for Groebner basis calculation in multivariate polynomial rings over fields. -AUTHORS: - -- William Stein - -- Kiran S. Kedlaya (2006-02-12): added Macaulay2 analogues of some - Singular features - -- Martin Albrecht (2008,2007): refactoring, many Singular related - functions - -- Martin Albrecht (2009): added Groebner basis over rings - functionality from Singular 3.1 - -- John Perry (2012): bug fixing equality & containment of ideals - EXAMPLES: We compute a Groebner basis for some given ideal. The type returned by @@ -212,6 +196,21 @@ generators. For sequences of multivariate polynomials see :class:`sage.rings.polynomial.multi_polynomial_sequence.PolynomialSequence_generic`. +AUTHORS: + +- William Stein: initial version + +- Kiran S. Kedlaya (2006-02-12): added Macaulay2 analogues of some Singular + features + +- Martin Albrecht (2007,2008): refactoring, many Singular related functions, + added plot() + +- Martin Albrecht (2009): added Groebner basis over rings functionality from + Singular 3.1 + +- John Perry (2012): bug fixing equality & containment of ideals + """ #***************************************************************************** @@ -4786,15 +4785,19 @@ def plot(self, *args, **kwds): sage: I.plot() # the Singular logo Graphics object consisting of 1 graphics primitive - This used to be :trac:`5267`:: + :: - sage: I = R.ideal([-x^2*y+1]) - sage: I.plot() + sage: R. = PolynomialRing(QQ,2) + sage: I = R.ideal([x - 1]) + sage: I.plot((y, -2, 2)) # vertical line Graphics object consisting of 1 graphics primitive - AUTHORS: + :: + + sage: I = R.ideal([-x^2*y + 1]) + sage: I.plot() # blow up + Graphics object consisting of 1 graphics primitive - - Martin Albrecht (2008-09) """ from sage.rings.real_mpfr import RR from sage.plot.all import implicit_plot @@ -4808,7 +4811,7 @@ def plot(self, *args, **kwds): f = self.gens()[0] - variables = sorted(f.variables(), reverse=True) + variables = sorted(f.parent().gens(), reverse=True) if len(variables) == 2 and kwds.get('algorithm','') != 'surf': V = [(variables[0], None, None), (variables[1], None, None)] @@ -4833,14 +4836,14 @@ def plot(self, *args, **kwds): if V[var_index][1] is None: v, mi, ma = variables[var_index], -10, 10 for i in range(mi, ma): - roots = f.subs({v:i}).univariate_polynomial().change_ring(RR).roots() - if len(roots) > 0: + poly = f.subs({v:i}).univariate_polynomial().change_ring(RR) + if not poly or len(poly.roots()) > 0: mi = i - 1 break for i in range(ma, mi, -1): - roots = f.subs({v:i}).univariate_polynomial().change_ring(RR).roots() - if len(roots) > 0: + poly = f.subs({v:i}).univariate_polynomial().change_ring(RR) + if not poly or len(poly.roots()) > 0: ma = i + 1 break V[var_index] = variables[var_index], mi, ma From 97fef26ecbd6d0f326f393199e96ab32bd2f73d4 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Mon, 24 Aug 2020 14:49:39 +0530 Subject: [PATCH 329/379] minor correction --- src/sage/graphs/base/boost_graph.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 8fa6609f076..7ef29740a75 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2738,8 +2738,9 @@ cpdef shortest_paths_from_vertices(g, vertex_list=None, order=None, if order is not None: if len(g) == len(order): - if any(v not in order for v in g): - raise ValueError("Given ordering is not valid") + for vertex in order: + if vertex not in g: + raise ValueError("Given ordering is not valid") else: raise ValueError("Given ordering is not valid") From 0efe01641d7627bfac40f210436a649da350acb9 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 24 Aug 2020 10:27:58 +0100 Subject: [PATCH 330/379] enable non-x86_64 Debian/Ubuntu archs --- .../cvxopt/patches/libsuitesparse_path.patch | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 build/pkgs/cvxopt/patches/libsuitesparse_path.patch diff --git a/build/pkgs/cvxopt/patches/libsuitesparse_path.patch b/build/pkgs/cvxopt/patches/libsuitesparse_path.patch new file mode 100644 index 00000000000..fc8908aa7fa --- /dev/null +++ b/build/pkgs/cvxopt/patches/libsuitesparse_path.patch @@ -0,0 +1,17 @@ +diff --git a/setup.py b/setup.py +index d312416..4fa14c4 100644 +--- a/setup.py ++++ b/setup.py +@@ -58,9 +58,9 @@ if sys.platform.startswith("darwin"): + SUITESPARSE_LIB_DIR = '/usr/local/lib' + SUITESPARSE_INC_DIR = '/usr/local/include' + else: +- if glob("/usr/lib/x86_64-linux-gnu/libsuitesparse*"): +- # Ubuntu/Debian +- SUITESPARSE_LIB_DIR = "/usr/lib/x86_64-linux-gnu" ++ SUITESPARSE_LIB_SUFF = glob("/usr/lib/*/libsuitesparse*") ++ if SUITESPARSE_LIB_SUFF: # Ubuntu/Debian ++ SUITESPARSE_LIB_DIR = "/usr/lib/"+SUITESPARSE_LIB_SUFF[0].split('/')[3] + SUITESPARSE_INC_DIR = "/usr/include/suitesparse" + elif glob("/usr/lib64/libsuitesparse*"): + # CentOS/Fedora/RedHat From 4b11a706e1267b385099d8691b4468efd974f94d Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 24 Aug 2020 10:57:33 +0100 Subject: [PATCH 331/379] use the correct import location, not six.* --- src/sage_setup/docbuild/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/utils.py b/src/sage_setup/docbuild/utils.py index 272f7e2d0f3..9fce8e4edae 100644 --- a/src/sage_setup/docbuild/utils.py +++ b/src/sage_setup/docbuild/utils.py @@ -113,7 +113,7 @@ def build_many(target, args, processes=None): WorkerDiedException: worker for 4 died with non-zero exit code -9 """ from multiprocessing import Process, Queue, cpu_count - from six.moves.queue import Empty + from queue import Empty if processes is None: processes = cpu_count() From 1d0d255f1e66ef383942de9d9793753463ba169b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Aug 2020 12:10:18 +0200 Subject: [PATCH 332/379] use libgap in combinat/designs --- src/sage/combinat/designs/block_design.py | 32 +++++++---------- .../combinat/designs/incidence_structures.py | 36 ++++++++++++------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index dad5da3fb36..daa92d38376 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -51,11 +51,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import - from sage.modules.free_module import VectorSpace from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -65,6 +62,7 @@ from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown from sage.matrix.matrix_space import MatrixSpace +from sage.libs.gap.libgap import libgap BlockDesign = IncidenceStructure @@ -275,14 +273,13 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, ch B.relabel({i:p[0] for p,i in points.items()}) elif algorithm == "gap": # Requires GAP's Design - from sage.interfaces.gap import gap - gap.load_package("design") - gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,F.order(),d)) - v = eval(gap.eval("D.v")) - gblcks = eval(gap.eval("D.blocks")) + libgap.load_package("design") + D = libgap.PGPointFlatBlockDesign(n, F.order(), d) + v = D['v'].sage() + gblcks = D['blocks'].sage() gB = [] for b in gblcks: - gB.append([x-1 for x in b]) + gB.append([x - 1 for x in b]) B = BlockDesign(v, gB, name="ProjectiveGeometryDesign", check=check) if check: @@ -910,6 +907,7 @@ def CremonaRichmondConfiguration(): H.relabel() return H + def WittDesign(n): """ INPUT: @@ -936,20 +934,16 @@ def WittDesign(n): sage: print(BD) # optional - gap_packages (design package) Incidence structure with 9 points and 12 blocks """ - from sage.interfaces.gap import gap - gap.load_package("design") - gap.eval("B:=WittDesign(%s)"%n) - v = eval(gap.eval("B.v")) - gblcks = eval(gap.eval("B.blocks")) - gB = [] - for b in gblcks: - gB.append([x-1 for x in b]) + libgap.load_package("design") + B = libgap.WittDesign(n) + v = B['v'].sage() + gB = [[x - 1 for x in b] for b in B['blocks'].sage()] return BlockDesign(v, gB, name="WittDesign", check=True) def HadamardDesign(n): """ - As described in Section 1, p. 10, in [CvL]. The input n must have the + As described in Section 1, p. 10, in [CvL]_. The input n must have the property that there is a Hadamard matrix of order `n+1` (and that a construction of that Hadamard matrix has been implemented...). diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 12eb87c6c31..1776fde1b0d 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -38,11 +38,10 @@ # the License, or (at your option) any later version. # # https://www.gnu.org/licenses/ # # ************************************************************************** -from __future__ import print_function - from sage.rings.integer import Integer from sage.misc.latex import latex from sage.sets.set import Set +from sage.libs.gap.libgap import libgap class IncidenceStructure(object): @@ -1058,6 +1057,22 @@ def _gap_(self): gB = [[x + 1 for x in b] for b in self._blocks] return "BlockDesign({},{})".format(v, gB) + def _libgap_(self): + """ + Return the design as a GAP record. + + EXAMPLES:: + + sage: D = IncidenceStructure(4, [[0,2],[1,2,3],[2,3]]) + sage: D._libgap_() + rec( blocks := [ [ 1, 3 ], [ 2, 3, 4 ], [ 3, 4 ] ], + isBlockDesign := true, v := 4 ) + """ + libgap.load_package("design") + v = self.num_points() + gB = [[x + 1 for x in b] for b in self._blocks] + return libgap.BlockDesign(v, gB) + def intersection_graph(self, sizes=None): r""" Return the intersection graph of the incidence structure. @@ -1741,7 +1756,7 @@ def dual(self, algorithm=None): .. NOTE:: The ``algorithm="gap"`` option requires GAP's Design package - (included in the gap_packages Sage spkg). + (included in the ``gap_packages`` Sage spkg). EXAMPLES: @@ -1772,18 +1787,13 @@ def dual(self, algorithm=None): REFERENCE: - Soicher, Leonard, Design package manual, available at - http://www.gap-system.org/Manuals/pkg/design/htm/CHAP003.htm + https://www.gap-system.org/Manuals/pkg/design/htm/CHAP003.htm """ if algorithm == "gap": - from sage.interfaces.gap import gap - gap.load_package("design") - gD = self._gap_() - gap.eval("DD:=DualBlockDesign("+gD+")") - v = eval(gap.eval("DD.v")) - gblcks = eval(gap.eval("DD.blocks")) - gB = [] - for b in gblcks: - gB.append([x-1 for x in b]) + libgap.load_package("design") + DD = libgap(self).DualBlockDesign() + v = DD['v'].sage() + gB = [[x - 1 for x in b] for b in DD['blocks'].sage()] return IncidenceStructure(list(range(v)), gB, name=None, check=False) else: return IncidenceStructure( From dc4612747fca011a4d8abcf2fbc118e7be78f672 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Mon, 24 Aug 2020 12:49:08 +0200 Subject: [PATCH 333/379] #30353 : doctest for #30353 --- src/sage/symbolic/integration/integral.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 80bc14679c7..8c595e42764 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -952,6 +952,14 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): sage: integrate(sqrt(1-4*sin(x)^2),x, algorithm='maxima') integrate(sqrt(-4*sin(x)^2 + 1), x) + + Check that :trac:`30353` is fixed:: + + sage: a = SR.var('a') + sage: assume(a > 0) + sage: assume(a < 1) + sage: integrate(x*log(1/(a*x+(1-x)^2)), x, 0, 1, algorithm='maxima') + 1/4*a^2*log(a) + 1/2*sqrt(-a^2 + 4*a)*a*arctan(sqrt(-a^2 + 4*a)*(a - 2)/(a^2 - 4*a)) - 1/2*sqrt(-a^2 + 4*a)*a*arctan(sqrt(-a^2 + 4*a)/(a - 4)) - a*log(a) - sqrt(-a^2 + 4*a)*arctan(sqrt(-a^2 + 4*a)*(a - 2)/(a^2 - 4*a)) + sqrt(-a^2 + 4*a)*arctan(sqrt(-a^2 + 4*a)/(a - 4)) - 1/2*a + 3/2 """ expression, v, a, b = _normalize_integral_input(expression, v, a, b) if algorithm is not None: From fe71c17ac3270f68eae91648f90cadcabaa48d47 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Mon, 24 Aug 2020 13:22:06 +0200 Subject: [PATCH 334/379] #28538 : add doctest for #28538 --- src/sage/symbolic/assumptions.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index 57145ec167c..c0300286574 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -648,6 +648,13 @@ def assume(*args): [0 < x] sage: forget() + Chack that :trac:`28538` is fixed:: + + sage: x, y = SR.var('x, y') + sage: assume(x > 0) + sage: assume(y > 0) + sage: bool(y*(x - y) == 0) + False """ for x in preprocess_assumptions(args): if isinstance(x, (tuple, list)): From f6030d4cbbe8a926e867612b36064d39cfad9e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Aug 2020 14:37:37 +0200 Subject: [PATCH 335/379] tune details in combinat/designs --- src/sage/combinat/designs/block_design.py | 1 - src/sage/combinat/designs/incidence_structures.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index daa92d38376..3f78fcf5bf9 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -741,7 +741,6 @@ def projective_plane(n, check=True, existence=False): sage: designs.projective_plane(12, existence=True) Unknown """ - from sage.rings.sum_of_squares import is_sum_of_two_squares_pyx from sage.combinat.designs.bibd import BruckRyserChowla_check if n <= 1: diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 1776fde1b0d..6d144bd1d10 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1064,7 +1064,7 @@ def _libgap_(self): EXAMPLES:: sage: D = IncidenceStructure(4, [[0,2],[1,2,3],[2,3]]) - sage: D._libgap_() + sage: D._libgap_() # optional - gap_packages rec( blocks := [ [ 1, 3 ], [ 2, 3, 4 ], [ 3, 4 ] ], isBlockDesign := true, v := 4 ) """ From 80218208bfbcfc44aad6a05bd65db60504f86f68 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 24 Aug 2020 09:17:22 -0400 Subject: [PATCH 336/379] changed yield from to for loops --- src/sage/graphs/generic_graph.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 0506ef2de48..908fddef5dd 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10478,12 +10478,18 @@ def vertex_iterator(self, vertices=None, degree=None, vertex_property=None): """ if degree: if vertex_property is not None: - yield from [v for v, d in self.degree_iterator(labels=True) if d == degree and vertex_property(v)] + for v, d in self.degree_iterator(labels=True): + if d == degree and vertex_property(v): + yield v else: - yield from [v for v, d in self.degree_iterator(labels=True) if d == degree] + for v, d in self.degree_iterator(labels=True): + if d == degree: + yield v elif vertex_property is not None: - yield from [v for v in self._backend.iterator_verts(vertices) if vertex_property(v)] + for v in self._backend.iterator_verts(vertices): + if vertex_property(v): + yield v else: for v in self._backend.iterator_verts(vertices): From bee08cac1a0999f863b197cb9b7802892cc1afa3 Mon Sep 17 00:00:00 2001 From: Marketa Slukova Date: Sun, 25 Aug 2019 16:16:34 +0200 Subject: [PATCH 337/379] Documentation changes to coding module. --- src/doc/en/reference/coding/index.rst | 29 ++++++++++++++++++----- src/sage/coding/abstract_code.py | 34 ++++++++++++++++++++++++++- src/sage/coding/linear_code.py | 2 +- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 7b83537c5a6..e737ba053d1 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -7,16 +7,13 @@ Coding theory is the mathematical theory for algebraic and combinatorial codes used for forward error correction in communications theory. Sage provides an extensive library of objects and algorithms in coding theory. -Basic objects in coding theory are channels, codes, linear codes, encoders, and +Basic objects in coding theory are codes, channels, encoders, and decoders. The following modules provide the base classes defining them. .. toctree:: :maxdepth: 1 sage/coding/abstract_code - sage/coding/linear_code_no_metric - sage/coding/linear_code - sage/coding/linear_rank_metric sage/coding/channel sage/coding/encoder sage/coding/decoder @@ -33,8 +30,28 @@ the parameters of linear codes are provided. sage/coding/encoders_catalog sage/coding/bounds_catalog -Families of Codes ------------------ + +Linear Codes +------------ + +The following module is a base class for linear code objects regardless their +metric. + +.. toctree:: + :maxdepth: 1 + + sage/coding/linear_code_no_metric + +There is a number of representatives of linear codes over a specific metric. + +.. toctree:: + :maxdepth: 1 + + sage/coding/linear_code + sage/coding/linear_rank_metric + +Families of Linear Codes +------------------------ Famous families of codes, listed below, are represented in Sage by their own classes. For some of them, implementations of special decoding algorithms or diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index 6ebd37677cc..b0bc941a931 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -1,9 +1,41 @@ r""" -Base class for Codes +Codes Class supporting methods available for any type of code (linear, non-linear) and over any metric (Hamming, rank). +There are further abstract classes representing certain types of codes. For +linear codes, +:class:`~sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric` contains +all the methods that any linear code can use regardless of its metric. +Inheriting from this class are base classes for linear codes over specific +metrics. For example, :class:`~sage.coding.linear_code.AbstractLinearCode` is a +base class for all linear codes over the Hamming metric. + +Take the class :class:`~sage.coding.hamming_code.HammingCode`. This +class inherits from :class:`~sage.coding.linear_code.AbstractLinearCode`, since +it is a linear code over the Hamming metric. +:class:`~sage.coding.linear_code.AbstractLinearCode` then inherits from +:class:`~sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric`, since it +is a linear code. Finally, this class inherits from +:class:`~sage.coding.abstract_code.AbstractCode`, since it is a code. + + +The following diagram shows the inheritance relationship in the coding module:: + + AbstractCode + + AbstractLinearCodeNoMetric + | + AbstractLinearCode + | | + ParityCheckCode + | | + HammingCode + | | + CyclicCode + | | + BCHCode + | | + GolayCode + | | + ReedMullerCode + | | + GeneralizedReedSolomonCode + | | + GoppaCode + | + AbstractLinearRankMetricCode + Any class inheriting from AbstractCode can use the encode/decode framework. The encoder/decoder framework within the coding module offers the creation and diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index edff7b33bcf..03aa4ba5720 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Linear codes +Generic structures for linear codes over the Hamming metric Linear Codes ============ From 2342a3a26e3b6077b643ee06b0b959f26bcdffd9 Mon Sep 17 00:00:00 2001 From: Marketa Slukova Date: Sun, 25 Aug 2019 16:16:57 +0200 Subject: [PATCH 338/379] Thematic tutorials updated. --- .../en/thematic_tutorials/coding_theory.rst | 77 +++++++++- .../structures_in_coding_theory.rst | 145 ++++++++++++------ 2 files changed, 172 insertions(+), 50 deletions(-) diff --git a/src/doc/en/thematic_tutorials/coding_theory.rst b/src/doc/en/thematic_tutorials/coding_theory.rst index 2a4fe5c55be..800421a2a82 100644 --- a/src/doc/en/thematic_tutorials/coding_theory.rst +++ b/src/doc/en/thematic_tutorials/coding_theory.rst @@ -9,6 +9,7 @@ Coding Theory in Sage .. MODULEAUTHOR:: David Joyner and Robert Miller (2008), edited by Ralf Stephan for the initial version. David Lucas (2016) for this version. + Marketa Slukova (2019) for the latest version. This tutorial, designed for beginners who want to discover how to use Sage @@ -21,7 +22,8 @@ During this tutorial, we will cover the following parts: - what can you do with **generic linear codes and associated methods**, - what can you do with **structured code families**, - what can you do to **encode and recover messages, correct errors** and -- what can you do to **easily add errors to codewords**. +- what can you do to **easily add errors to codewords**, +- what can you do with **codes in general**. The goal of this tutorial is to give a quick overview of what can be done with the library and how to use the main functionalities. @@ -36,7 +38,7 @@ for this class/method. I. Generic Linear codes and associated methods ============================================== -Let us start with the most generic code one can build: a generic linear code +Let us start with the most basic code one can build: a generic linear code without any specific structure. To build such a code, one just need to provide @@ -67,7 +69,7 @@ As we can a lot of things, let us start with the basic functionalities. In the example just above, we already asked for the code's generator matrix. It is also possible to ask the code for -its basic parameters: its *length* and *dimension* as illustrated therafter:: +its basic parameters: its *length* and *dimension* as illustrated thereafter:: sage: C.length() 7 @@ -115,13 +117,14 @@ If one wants to get all methods that can be run on a linear code, one can: If you're receiving an error message related to Gap, please check the documentation of the method to verify if Guava has to be installed. + II. Structured code families and an overview of the encoding and decoding system ================================================================================ II.1 Create specific codes in Sage ---------------------------------- -Now that we know how to create generic linear codes, we want to go deeper +Now that we know how to create generic codes, we want to go deeper and create specific code families. In Sage, all codes families can be accessed by typing:: @@ -556,8 +559,70 @@ Note it is guaranteed by construction that errors and erasures will never overlap, so when you ask for ``e`` errors and ``t`` erasures, you will always receive a vector with ``e`` errors and ``t`` erased positions. -V. Conclusion - Afterword -========================= + +V. Codes in General +=================== + +So far we have talked only about codes which are linear and over the Hamming +metric. Sage also supports codes which are non-linear and/or over a different +metric. Since working with these usually involves creating new classes, they are +covered in detail in :ref:`structures_in_coding_theory`. + +For this tutorial, we include a small example of a linear code over the rank +metric. Unlike the Hamming metric, where the distance between two words is the +number of positions in which their differ, rank metric takes distance to be the +rank of the difference of two codewords, which are represented as matrices. + +We are going to create a generic linear code over the rank metric without any +additional structure. To build such a code, we only need a generator matrix:: + + sage: G = Matrix(GF(4), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G) + +We can do all the things that we did with our example of a linear code over the +Hamming metric. Therefore we are going to focus on the different metric. + +Take a word in our code:: + + sage: c = C[1] + sage: c + (1, 1, 0) + +Over the usual Hamming metric, the weight of this word would be `2`. However, +over the rank metric, we get a different result:: + + sage: C.rank_weight_of_vector(c) + 1 + +The weight of a word in the rank metric is simply the rank of the matrix form of +the word:: + + sage: C.matrix_form_of_vector(c) + [1 1 0] + [0 0 0] + +As we said before, the distance between two words is the rank of their +difference:: + + sage: d = C[2] + sage: d + (z2, z2, 0) + sage: C.rank_distance_between_vectors(c, d) + 1 + +Even though the words `c` and `d` differ in two positions, their distance over +the rank metric is `1`:: + + sage: C.matrix_form_of_vector(c - d) + [1 1 0] + [1 1 0] + +For more details on the linear rank metric code, see +:ref:`sage.coding.linear_rank_metric`. + + +VI. Conclusion - Afterword +========================== This last section concludes our tutorial on coding theory. diff --git a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst index 4726bce061a..ab558b21b34 100644 --- a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst +++ b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst @@ -6,7 +6,8 @@ How to write your own classes for coding theory =============================================== -.. MODULEAUTHOR:: David Lucas +.. MODULEAUTHOR:: David Lucas (2016) for initial version. Marketa Slukova (2019) + for this version. This tutorial, designed for advanced users who want to build their own classes, will explain step by step what you need to do to write code which integrates @@ -18,18 +19,70 @@ During this tutorial, we will cover the following parts: - how to write a new **decoder** - how to write a new **channel** -Through all this tutorial, we will follow the same example, namely the -implementation of repetition code. At the end of each part, we will summarize -every important step of the implementation. If one just wants -a quick access to the implementation of one of the objects cited above, one can -jump directly to the end of related part, +This tutorial focuses on the example of implementation of the repetition code. +At the end of each part, we will summarize every important step of the +implementation. If one just wants a quick access to the implementation of one of +the objects cited above, one can jump directly to the end of related part, which presents a summary of what to do. .. contents:: Table of contents :depth: 2 -I. The repetition code -====================== +I. Abstract classes +=================== + +There is a number of abstract classes representing different types of codes in +the coding module of Sage. Depending on the type of code you want to implement, +you should inherit from different abstract classes. + +The most generic class is :class:`sage.coding.abstract_code:AbstractCode`. This +class makes no assumptions about linearity, metric, finiteness or the number of +alphabets. The abstract notion of "code" that is implicitly used for this class +is any enumerable subset of a cartesian product +`A_1 \times A_2 \times \ldots \times A_n` for some sets `A_i`. + +If, for example, you want to create a non-linear code family, this is the class +you should inherit from. + +We give a small example of creating a non-linear code family. Take the code +consisting of the following `l` words: + +`\{00 \ldots 00, 10 \ldots 00, 11 \ldots 00, \ldots , 11 \ldots 10, 11 \ldots 11 \}`. + +Here is how we can implement it:: + + sage: class ExampleCodeFamily(AbstractCode): + ....: def __init__(self, length): + ....: super(ExampleCodeFamily, self).__init__(length) + ....: def __iter__(self): + ....: for i in range(self.length() + 1): + ....: yield vector([1 for j in range(i)] + [0 for k in range(i, self.length())]) + ....: def __contains__(self, word): + ....: return word in list(self) + ....: def _repr_(self): + ....: return "Dummy code of length {}".format(self.length()) + +We can check that our iterator gives us the code that we wanted:: + + sage: C = ExampleCodeFamily(4) + sage: C.list() + [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 1)] + +Focusing on linear codes, the most generic representative is the class +:class:`sage.coding.linear_code_no_metric:AbstractLinearCodeNoMetric` which +contains all the methods that all linear codes share regardless of what their +metric is. If you want to implement a linear code over some metric which has +not been implemented in Sage yet, this is the class you should be inheriting +from. + +We have two metric specific abstract linear code classes, +:class:`sage.coding.linear_code:AbstractLinearCode` for Hamming metric and +:class:`sage.coding.linear_rank_metric:AbstractLinearRankMetricCode` for rank +metric. If you wish to implement a linear code class over one of these metrics, +you should inherit from the given abstract class. + +II. The repetition code +======================= We want to implement in Sage the well-known repetition code. Its definition follows: @@ -52,11 +105,15 @@ It can correct up to :math:`\left\lceil \frac{n-1}{2} \right\rceil` errors. Through all this tutorial, we will illustrate the implementation of the :math:`(n, 1)`-repetition code over :math:`\GF{2}`. -II. Write a new code class -========================== +III. Write a new code class +=========================== + +The first thing to do to write a new code class is to identify which abstract +class to inherit from. Since the repetition code is linear and we take it over +the Hamming metric, this means that we will inherit from +:class:`sage.coding.linear_code.AbstractLinearCode`. -The first thing to do to write a new code class is to identify -the following elements: +Now we have to identify the initializing parameters of this class, which are: - the length of the code, - the base field of the code, @@ -69,9 +126,9 @@ and one decoder. Now we isolated the parameters of the code, we can write the constructor of our class. -Every linear code class must inherit from +As we said, every linear code class over the Hamming metric must inherit from :class:`sage.coding.linear_code.AbstractLinearCode`. -This class provide a lot of useful methods and, as we illustrate thereafter, +This class provides a lot of useful methods and, as we illustrate thereafter, a default constructor which sets the *length*, the *base field*, the *default encoder* and the *default decoder* as class parameters. We also need to create the dictionary of known encoders and decoders @@ -113,30 +170,30 @@ We also write a method to check equality:: After these examples, you probably noticed that we use two methods, namely ``length()`` and ``dimension()`` without defining them. -That is because their implementation is provided in -:class:`sage.coding.linear_code.AbstractLinearCode`. -The abstract class provides a default implementation of the -following getter methods: - -- :meth:`sage.coding.linear_code.AbstractLinearCode.dimension` -- :meth:`sage.coding.linear_code.AbstractLinearCode.length`, -- :meth:`sage.coding.linear_code.AbstractLinearCode.base_field` and -- :meth:`sage.coding.linear_code.AbstractLinearCode.ambient_space`. - -It also provides an implementation of ``__ne__`` which returns the inverse -of ``__eq__`` and several other very useful methods, like ``__contains__``. -Note that a lot of these other methods rely on the computation of a generator -matrix. It is thus highly recommended to set an encoder which -knows how to compute such a matrix as default encoder. -As default encoder will be used by all these methods which expect a -generator matrix, if one provides a default encoder which does not have a -``generator_matrix`` method, a lot of generic methods will fail. +That is because their implementation is provided in parent classes of +:class:`sage.coding.linear_code.AbstractLinearCode`, which are +:class:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric` and +:class:`sage.coding.abstract_code.AbstractCode` + +They provide a default implementation of the following getter methods: + +- :meth:`sage.coding.abstract_code.AbstractCode.length`, +- :meth:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.dimension`, +- :meth:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.base_field` and +- :meth:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.ambient_space`. + +They also provide several other useful methods, such as ``__contains__``. Note +that a lot of these other methods rely on the computation of a generator matrix. +It is thus highly recommended to set an encoder which knows how to compute such +a matrix as default encoder. As default encoder will be used by all these +methods which expect a generator matrix, if one provides a default encoder which +does not have a ``generator_matrix`` method, a lot of generic methods will fail. As our code family is really simple, we do not need anything else, and the code provided above is enough to describe properly a repetition code. -Summary of the implementation for linear codes ----------------------------------------------- +Summary of the implementation for linear codes over the Hamming metric +---------------------------------------------------------------------- 1. Inherit from :class:`sage.coding.linear_code.AbstractLinearCode`. 2. Add ``_registered_encoders = {}`` and ``_registered_decoders = {}`` @@ -157,8 +214,8 @@ We now know how to write a new code class. Let us see how to write a new encoder and a new decoder. -III. Write a new encoder class -============================== +IV. Write a new encoder class +============================= Let us continue our example. We ask the same question as before: what do we need to describe the encoder? @@ -280,8 +337,8 @@ Summary of the implementation for encoders 8. Add the encoder to ``CodeClass._registered_encoders``. -IV. Write a new decoder class -============================== +V. Write a new decoder class +============================ Let us continue by writing a decoder. As before, we need to know what is required to describe a decoder. We need of course the associated code of @@ -412,8 +469,8 @@ Summary of the implementation for decoders ``decoding_radius``. 7. Add the encoder to ``CodeClass._registered_decoders``. -V. Write a new channel class -============================ +VI. Write a new channel class +============================= Alongside all these new structures directly related to codes, we also propose a whole new and shiny structure to experiment on codes, and more specifically @@ -517,8 +574,8 @@ Summary of the implementation for channels 5. Override ``transmit_unsafe``. -VI. Sort our new elements -========================= +VII. Sort our new elements +========================== As there is many code families and channels in the coding theory library, we do not wish to store all our classes directly in Sage's global namespace. @@ -559,8 +616,8 @@ Here it means the following: from sage.coding.channel import BinaryStaticErrorRateChannel -VII. Complete code of this tutorial -=================================== +VIII. Complete code of this tutorial +==================================== If you need some base code to start from, feel free to copy-paste and derive from the one that follows. From 9641e926cb1e0dd58bacb4697b5ccac274badbac Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 24 Aug 2020 14:30:04 +0100 Subject: [PATCH 339/379] follow pyflakes advise --- src/sage/coding/abstract_code.py | 5 ----- src/sage/coding/linear_code.py | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index b0bc941a931..525bb6e63ec 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -80,8 +80,6 @@ class inherits from :class:`~sage.coding.linear_code.AbstractLinearCode`, since from sage.structure.parent import Parent from sage.misc.cachefunc import cached_method from copy import copy -from .encoder import Encoder -from .decoder import Decoder, DecodingError from sage.rings.integer import Integer import inspect @@ -291,9 +289,6 @@ def __init__(self, length, default_encoder_name=None, ValueError: length must be a non-zero positive integer """ - _registered_encoders = {} - _registered_decoders = {} - if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") if length <= 0: diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 03aa4ba5720..0ce0bc86f43 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -940,7 +940,8 @@ def divisor(self): V = VectorSpace(QQ,n+1) S = V(A).nonzero_positions() S0 = [S[i] for i in range(1,len(S))] - if len(S)>1: return GCD(S0) + if len(S)>1: + return GCD(S0) return 1 def is_projective(self): @@ -1001,7 +1002,8 @@ def direct_sum(self, other): sage: C3 = C1.direct_sum(C2); C3 [21, 12] linear code over GF(2) """ - C1 = self; C2 = other + C1 = self + C2 = other G1 = C1.generator_matrix() G2 = C2.generator_matrix() F = C1.base_ring() From 5a750a24fdf51a439eaa1e83efbf3fb4d6383a96 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 24 Aug 2020 10:20:49 -0400 Subject: [PATCH 340/379] changed last for loop back to yield from --- src/sage/graphs/generic_graph.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 908fddef5dd..fb39f0e41a5 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10492,8 +10492,7 @@ def vertex_iterator(self, vertices=None, degree=None, vertex_property=None): yield v else: - for v in self._backend.iterator_verts(vertices): - yield v + yield from self._backend.iterator_verts(vertices) __iter__ = vertex_iterator From d44d6778a09a2e1fa2ee5187b72dfe0beb005515 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 24 Aug 2020 15:27:43 +0100 Subject: [PATCH 341/379] fix a doctest in the tutorial --- .../structures_in_coding_theory.rst | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst index ab558b21b34..3e5b6857c1e 100644 --- a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst +++ b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst @@ -1,13 +1,11 @@ .. -*- coding: utf-8 -*- - .. _structures_in_coding_theory: =============================================== How to write your own classes for coding theory =============================================== -.. MODULEAUTHOR:: David Lucas (2016) for initial version. Marketa Slukova (2019) - for this version. +.. MODULEAUTHOR:: David Lucas (2016) for initial version. Marketa Slukova (2019) for this version. This tutorial, designed for advanced users who want to build their own classes, will explain step by step what you need to do to write code which integrates @@ -51,20 +49,19 @@ consisting of the following `l` words: Here is how we can implement it:: + sage: from sage.coding.abstract_code import AbstractCode sage: class ExampleCodeFamily(AbstractCode): - ....: def __init__(self, length): - ....: super(ExampleCodeFamily, self).__init__(length) - ....: def __iter__(self): - ....: for i in range(self.length() + 1): - ....: yield vector([1 for j in range(i)] + [0 for k in range(i, self.length())]) - ....: def __contains__(self, word): - ....: return word in list(self) - ....: def _repr_(self): - ....: return "Dummy code of length {}".format(self.length()) - -We can check that our iterator gives us the code that we wanted:: - - sage: C = ExampleCodeFamily(4) + ....: def __init__(self, length): + ....: super(ExampleCodeFamily, self).__init__(length) + ....: def __iter__(self): + ....: for i in range(self.length() + 1): + ....: yield vector([1 for j in range(i)] + [0 for k in range(i, self.length())]) + ....: def __contains__(self, word): + ....: return word in list(self) + ....: def _repr_(self): + ....: return "Dummy code of length {}".format(self.length()) + + sage: C = ExampleCodeFamily(4) # check that this works. sage: C.list() [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 1)] From 2218aac9a0fab5bc6114df081a25c8236eb811bb Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sun, 5 Jul 2020 09:36:40 -0300 Subject: [PATCH 342/379] Revert _test_jacobi to some_elements instead of all gens * Even using self.gens() we may not get all the generators as Lie Conformal algebras (eg. if self is a PoissonVertexAlgebra). * Make sure that a finitely generated Lie conformal algebra returns at least all generators with `some_elements` --- ...nitely_generated_lie_conformal_algebras.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 187e8c8065e..0dc5bc5cddc 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -32,6 +32,25 @@ class FinitelyGeneratedLieConformalAlgebras(CategoryWithAxiom_over_base_ring): """ _base_category_class_and_axiom = (LieConformalAlgebras, "FinitelyGeneratedAsLambdaBracketAlgebra") + def some_elements(self): + """ + Some elements of this Lie conformal algebra. + + Returns a list with elements containing at least the + generators. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Affine(QQ, 'A1', names=('e', 'h', 'f')) + sage: V.some_elements() + [e, h, f, K, Th + 4*T^(2)e, 4*T^(2)h, Te + 4*T^(2)e, Te + 4*T^(2)h] + """ + S = list(self.gens()) + from sage.misc.misc import some_tuples + for x,y in some_tuples(S, 2, 0, max_samples=self.ngens()): + S.append(x.T() + 2*y.T(2)) + return S + class Super(SuperModulesCategory): """ The category of super finitely generated Lie conformal algebras. From cc073cd4f20871725abe786f7daa3526ac789d6c Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Wed, 1 Jul 2020 22:14:02 -0300 Subject: [PATCH 343/379] Implemented some examples of Lie conformal algebras --- .../algebras/lie_conformal_algebras.rst | 7 + .../abelian_lie_conformal_algebra.py | 111 ++++++++++ .../bosonic_ghosts_lie_conformal_algebra.py | 129 +++++++++++ .../lie_conformal_algebras/examples.py | 21 ++ .../fermionic_ghosts_lie_conformal_algebra.py | 126 +++++++++++ .../free_bosons_lie_conformal_algebra.py | 170 +++++++++++++++ .../free_fermions_lie_conformal_algebra.py | 159 ++++++++++++++ .../n2_lie_conformal_algebra.py | 114 ++++++++++ .../weyl_lie_conformal_algebra.py | 206 ++++++++++++++++++ 9 files changed, 1043 insertions(+) create mode 100644 src/sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py create mode 100644 src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py diff --git a/src/doc/en/reference/algebras/lie_conformal_algebras.rst b/src/doc/en/reference/algebras/lie_conformal_algebras.rst index 0752d5843b7..7df4056f0b1 100644 --- a/src/doc/en/reference/algebras/lie_conformal_algebras.rst +++ b/src/doc/en/reference/algebras/lie_conformal_algebras.rst @@ -19,9 +19,16 @@ Implemented examples of Lie Conformal Algebras .. toctree:: + ../sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra ../sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra + ../sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra See also diff --git a/src/sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py new file mode 100644 index 00000000000..730d1076483 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py @@ -0,0 +1,111 @@ +r""" +Abelian Lie Conformal Algebra + +For a commutative ring `R` and a free `R`-module `M`. The *Abelian Lie +conformal algebra* generated by `M` is the free `R[T]` module +generated by `M` with vanishing `\lambda`-brackets. + +AUTHORS: + +- Reimundo Heluani (2020-06-15): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra +from sage.structure.indexed_generators import standardize_names_index_set + +class AbelianLieConformalAlgebra(GradedLieConformalAlgebra): + r""" + The Abelian Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring; the base ring of this Lie + conformal algebra + - ``ngens`` -- a positive integer (default: ``1``); the number + of generators of this Lie conformal algebra + - ``weights`` -- a list of positive rational numbers (default: + ``1`` for each + generator); the weights of the generators. The resulting + Lie conformal algebra is `H`-graded. + - ``parity`` -- ``None`` or a list of ``0`` or ``1`` (default: + ``None``); The parity of the generators. If not ``None`` the + resulting Lie Conformal algebra is a Super Lie conformal + algebra + - ``names`` -- a tuple of ``str`` or ``None`` (default: ``None`` + ); the list of names of the generators of this algebra. + - ``index_set`` -- an enumerated set or ``None`` (default: + ``None``); A set indexing the generators of this Lie + conformal algebra. + + OUTPUT: + + The Abelian Lie conformal algebra with generators `a_i`, + `i=1,...,n` and vanishing `\lambda`-brackets, where `n` is + ``ngens``. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.Abelian(QQ,2); R + The Abelian Lie conformal algebra with generators (a0, a1) over Rational Field + sage: R.inject_variables() + Defining a0, a1 + sage: a0.bracket(a1.T(2)) + {} + + TESTS:: + + sage: R.central_elements() + () + sage: R.structure_coefficients() + Finite family {} + + .. TODO:: + + implement its own class to speed up arithmetics in this + case. + """ + def __init__(self, R, ngens=1, weights=None, + parity=None, names=None, index_set=None): + """ + Initialize self. + + EXAMPLES:: + + sage: V = lie_conformal_algebras.Abelian(QQ) + sage: TestSuite(V).run() + """ + if (names is None) and (index_set is None): + names = 'a' + self._latex_names = tuple(r'a_{%d}' % i for i in range(ngens)) + + names,index_set = standardize_names_index_set(names=names, + index_set=index_set, + ngens=ngens) + abeliandict = {} + + GradedLieConformalAlgebra.__init__(self, R, abeliandict, names=names, + index_set=index_set, weights=weights, + parity=parity) + + def _repr_(self): + """ + String representation. + + EXAMPLES:: + + sage: lie_conformal_algebras.Abelian(QQ) + The Abelian Lie conformal algebra with generators (a,) over Rational Field + """ + return "The Abelian Lie conformal algebra with generators {} over {}"\ + .format(self.gens(), self.base_ring()) + diff --git a/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py new file mode 100644 index 00000000000..9fce5572cdd --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py @@ -0,0 +1,129 @@ +r""" +Bosonic Ghosts Lie Conformal Algebra + +The *Bosonic-ghosts* or `\beta-\gamma`-system Lie conformal algebra +with `2n` generators is the H-graded Lie conformal algebra generated +by `\beta_i, \gamma_i, i = 1,\ldots,n` and a central element `K`, with +non-vanishing `\lambda`-brackets: + +.. MATH:: + + [{\beta_i}_\lambda \gamma_j] = \delta_{ij} K. + +The generators `\beta_i` have degree `1` while the generators `\gamma_i` +have degree `0`. + +AUTHORS: + +- Reimundo Heluani (2020-06-15): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.matrix.special import identity_matrix +from sage.structure.indexed_generators import standardize_names_index_set +from .graded_lie_conformal_algebra import GradedLieConformalAlgebra +class BosonicGhostsLieConformalAlgebra(GradedLieConformalAlgebra): + r""" + The Bosonic ghosts or `\beta-\gamma`-system Lie conformal + algebra. + + INPUT: + + - ``R`` -- a commutative ring. + - ``ngens`` -- an even positive Integer (default: ``2``); the + number of non-central generators of this Lie conformal + algebra. + - ``names`` -- a list of ``str``; alternative names for the + generators + - ``index_set`` -- an enumerated set; An indexing set for the + generators. + + OUTPUT: + + The Bosonic Ghosts Lie conformal algebra with generators + `\beta_i,\gamma_i, i=1,\ldots,n` and `K`, where `2n` is + ``ngens``. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.BosonicGhosts(QQ); R + The Bosonic ghosts Lie conformal algebra with generators (beta, gamma, K) over Rational Field + sage: R.inject_variables(); beta.bracket(gamma) + Defining beta, gamma, K + {0: K} + sage: beta.degree() + 1 + sage: gamma.degree() + 0 + + sage: R = lie_conformal_algebras.BosonicGhosts(QQbar, ngens = 4, names = 'abcd'); R + The Bosonic ghosts Lie conformal algebra with generators (a, b, c, d, K) over Algebraic Field + sage: R.structure_coefficients() + Finite family {('a', 'c'): ((0, K),), ('b', 'd'): ((0, K),), ('c', 'a'): ((0, -K),), ('d', 'b'): ((0, -K),)} + + TESTS:: + + sage: lie_conformal_algebras.BosonicGhosts(AA).category() + Category of finitely generated H-graded Lie conformal algebras with basis over Algebraic Real Field + """ + + def __init__(self, R, ngens=2, names=None, index_set=None): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.BosonicGhosts(QQ) + sage: TestSuite(V).run() + """ + from sage.rings.all import ZZ + try: + assert (ngens in ZZ and ngens > 0 and ngens % 2 == 0) + except AssertionError: + raise ValueError("ngens should be an even positive integer, " + + "got {}".format(ngens)) + latex_names = None + if (names is None) and (index_set is None): + from sage.misc.defaults import variable_names as varnames + from sage.misc.defaults import latex_variable_names as laxnames + names = varnames(ngens/2,'beta') + varnames(ngens/2,'gamma') + latex_names = tuple(laxnames(ngens/2,r'\beta') +\ + laxnames(ngens/2,r'\gamma')) + ('K',) + + names,index_set = standardize_names_index_set(names=names, + index_set=index_set, + ngens=ngens) + A = identity_matrix(R,ngens/2) + from sage.matrix.special import block_matrix + gram_matrix = block_matrix([[R.zero(),A],[-A,R.zero()]]) + ghostsdict = { (i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + index_set.rank(j)]}} for i in index_set for j in index_set} + weights = (1,)*(ngens//2) + (0,)*(ngens//2) + super(BosonicGhostsLieConformalAlgebra,self).__init__(R, + ghostsdict,names=names, + latex_names=latex_names, + index_set=index_set, + weights=weights, + central_elements=('K',)) + + def _repr_(self): + """ + String representation. + + EXAMPLES:: + + sage: lie_conformal_algebras.BosonicGhosts(QQbar) + The Bosonic ghosts Lie conformal algebra with generators (beta, gamma, K) over Algebraic Field + """ + return "The Bosonic ghosts Lie conformal algebra with generators {} "\ + "over {}".format(self.gens(),self.base_ring()) + diff --git a/src/sage/algebras/lie_conformal_algebras/examples.py b/src/sage/algebras/lie_conformal_algebras/examples.py index 06283e2cb9f..3d520e449db 100644 --- a/src/sage/algebras/lie_conformal_algebras/examples.py +++ b/src/sage/algebras/lie_conformal_algebras/examples.py @@ -3,10 +3,17 @@ We implement the following examples of Lie conformal algebras: +- :mod:`Abelian Lie conformal algebra<.abelian_lie_conformal_algebra>` - :mod:`Affine Lie conformal algebra<.affine_lie_conformal_algebra>` +- :mod:`Bosonic Ghosts<.bosonic_ghosts_lie_conformal_algebra>` +- :mod:`Fermionic Ghosts<.fermionic_ghosts_lie_conformal_algebra>` +- :mod:`Free Bosons<.free_bosons_lie_conformal_algebra>` +- :mod:`Free Fermions<.free_fermions_lie_conformal_algebra>` +- :mod:`N=2 super Lie Conformal algebra<.n2_lie_conformal_algebra>` - :mod:`Neveu-Schwarz super Lie conformal algebra<.neveu_schwarz_lie_conformal_algebra>` - :mod:`Virasoro Lie conformal algebra<.virasoro_lie_conformal_algebra>` +- :mod:`Weyl Lie conformal algebra<.weyl_lie_conformal_algebra>` AUTHORS: @@ -23,10 +30,24 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from .abelian_lie_conformal_algebra import AbelianLieConformalAlgebra as Abelian from .affine_lie_conformal_algebra import AffineLieConformalAlgebra as Affine +from .bosonic_ghosts_lie_conformal_algebra import BosonicGhostsLieConformalAlgebra as BosonicGhosts +from .fermionic_ghosts_lie_conformal_algebra import FermionicGhostsLieConformalAlgebra as FermionicGhosts +from .free_bosons_lie_conformal_algebra import FreeBosonsLieConformalAlgebra as FreeBosons +from .free_fermions_lie_conformal_algebra import FreeFermionsLieConformalAlgebra as FreeFermions +from .n2_lie_conformal_algebra import N2LieConformalAlgebra as N2 from .neveu_schwarz_lie_conformal_algebra import NeveuSchwarzLieConformalAlgebra as NeveuSchwarz from .virasoro_lie_conformal_algebra import VirasoroLieConformalAlgebra as Virasoro +from .weyl_lie_conformal_algebra import WeylLieConformalAlgebra as Weyl +assert Abelian assert Affine +assert BosonicGhosts +assert FermionicGhosts +assert FreeBosons +assert FreeFermions +assert N2 assert NeveuSchwarz assert Virasoro +assert Weyl diff --git a/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py new file mode 100644 index 00000000000..11421cfdbd2 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py @@ -0,0 +1,126 @@ +r""" +Fermionic Ghosts Super Lie Conformal Algebra + +The *Fermionic-ghosts* or b--c system super Lie conformal algebra +with `2n` generators is the H-graded super Lie conformal algebra +generated by odd vectors `b_i, c_i, i = 1,\ldots,n` and a central +element `K`, with non-vanishing `\lambda`-brackets: + +.. MATH:: + + [{b_i}_\lambda c_j] = \delta_{ij} K. + +The generators `b_i` have degree `1` while the generators `c_i` +have degree `0`. + +AUTHORS: + +- Reimundo Heluani (2020-06-03): Initial implementation. +""" +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra + +class FermionicGhostsLieConformalAlgebra(GradedLieConformalAlgebra): + r""" + The Fermionic ghosts or `bc`-system super Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring; the base ring of this Lie + conformal algebra + - ``ngens`` -- an even positive Integer (default: ``2``); The + number of non-central generators of this Lie conformal + algebra. + - ``names`` -- a tuple of ``str``; alternative names for the + generators + - ``index_set`` -- an enumerated set; alternative indexing + set for the generators. + + OUTPUT: + + The Fermionic Ghosts super Lie conformal algebra with generators + `b_i,c_i, i=1,\ldots,n` and `K` where `2n` is ``ngens``. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.FermionicGhosts(QQ); R + The Fermionic ghosts Lie conformal algebra with generators (b, c, K) over Rational Field + sage: R.inject_variables() + Defining b, c, K + sage: b.bracket(c) == c.bracket(b) + True + sage: b.degree() + 1 + sage: c.degree() + 0 + sage: R.category() + Category of finitely generated super H-graded Lie conformal algebras with basis over Rational Field + + sage: R = lie_conformal_algebras.FermionicGhosts(QQbar, ngens=4, names = 'abcd');R + The Fermionic ghosts Lie conformal algebra with generators (a, b, c, d, K) over Algebraic Field + sage: R.structure_coefficients() + Finite family {('a', 'c'): ((0, K),), ('b', 'd'): ((0, K),), ('c', 'a'): ((0, K),), ('d', 'b'): ((0, K),)} + """ + def __init__(self,R,ngens=2,names=None,index_set=None): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.BosonicGhosts(QQ) + sage: TestSuite(V).run() + """ + try: + assert (ngens > 0 and ngens % 2 == 0) + except AssertionError: + raise ValueError("ngens should be an even positive integer, " + + "got {}".format(ngens)) + latex_names = None + if (names is None) and (index_set is None): + from sage.misc.defaults import variable_names as varnames + from sage.misc.defaults import latex_variable_names as laxnames + names = varnames(ngens/2,'b') + varnames(ngens/2,'c') + latex_names = tuple(laxnames(ngens/2,'b') +\ + laxnames(ngens/2,'c')) + ('K',) + + from sage.structure.indexed_generators import \ + standardize_names_index_set + names,index_set = standardize_names_index_set(names=names, + index_set=index_set, + ngens=ngens) + from sage.matrix.special import identity_matrix + A = identity_matrix(R,ngens/2) + from sage.matrix.special import block_matrix + gram_matrix = block_matrix([[R.zero(),A],[A,R.zero()]]) + ghostsdict = { (i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + index_set.rank(j)]}} for i in index_set for j in index_set} + weights = (1,)*(ngens//2) + (0,)*(ngens//2) + parity = (1,)*ngens + super(FermionicGhostsLieConformalAlgebra,self).__init__(R, + ghostsdict,names=names, + latex_names=latex_names, + index_set=index_set, + weights=weights, + parity=parity, + central_elements=('K',)) + + def _repr_(self): + """ + String representation. + + EXAMPLES:: + + sage: lie_conformal_algebras.FermionicGhosts(QQ) + The Fermionic ghosts Lie conformal algebra with generators (b, c, K) over Rational Field + """ + return "The Fermionic ghosts Lie conformal algebra with generators {} "\ + "over {}".format(self.gens(),self.base_ring()) diff --git a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py new file mode 100644 index 00000000000..45849316534 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py @@ -0,0 +1,170 @@ +r""" +Free Bosons Lie Conformal Algebra + +Given an `R`-module `M` with a symmetric, bilinear pairing +`(\cdot, \cdot): M\otimes_R M \rightarrow R`. The *Free Bosons* +Lie conformal algebra associated to this datum is the free +`R[T]`-module generated by `M` plus a central vector `K` satisfying +`TK=0`. The remaining `\lambda`-brackets are given by: + +.. MATH:: + + [v_\lambda w] = \lambda (v,w)K, + +where `v,w \in M`. + +This is an H-graded Lie conformal algebra where every generator +`v \in M` has degree 1. + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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.matrix.special import identity_matrix +from .graded_lie_conformal_algebra import GradedLieConformalAlgebra +from sage.structure.indexed_generators import standardize_names_index_set + +class FreeBosonsLieConformalAlgebra(GradedLieConformalAlgebra): + r""" + The Free Bosons Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring. + - ``ngens`` -- a positive Integer (default ``1``); the number of + non-central generators of this Lie conformal algebra. + - ``gram_matrix``: a symmetric square matrix with coefficients + in ``R`` (default: ``identity_matrix(ngens)``); the Gram + matrix of the inner product + - ``names`` -- a tuple of ``str``; alternative names for the + generators + - ``index_set`` -- an enumerated set; alternative indexing set + for the generators. + + OUTPUT: + + The Free Bosons Lie conformal algebra with generators + `\alpha_i`, `i=1,...,n` and `\lambda`-brackets + + .. MATH:: + + [{\alpha_i}_{\lambda} \alpha_j] = \lambda M_{ij} K, + + where `n` is the number of generators ``ngens`` and `M` is + the ``gram_matrix``. This Lie conformal + algebra is `H`-graded where every generator has conformal weight + `1`. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.FreeBosons(AA); R + The free Bosons Lie conformal algebra with generators (alpha, K) over Algebraic Real Field + sage: R.inject_variables() + Defining alpha, K + sage: alpha.bracket(alpha) + {1: K} + sage: M = identity_matrix(QQ,2); R = lie_conformal_algebras.FreeBosons(QQ,gram_matrix=M, names='alpha,beta'); R + The free Bosons Lie conformal algebra with generators (alpha, beta, K) over Rational Field + sage: R.inject_variables(); alpha.bracket(beta) + Defining alpha, beta, K + {} + sage: alpha.bracket(alpha) + {1: K} + sage: R = lie_conformal_algebras.FreeBosons(QQbar, ngens=3); R + The free Bosons Lie conformal algebra with generators (alpha0, alpha1, alpha2, K) over Algebraic Field + + TESTS:: + sage: R = lie_conformal_algebras.FreeBosons(QQ); R.0.degree() + 1 + sage: R = lie_conformal_algebras.FreeBosons(QQbar, ngens=2, gram_matrix=identity_matrix(QQ,1,1)) + Traceback (most recent call last): + ... + ValueError: the gram_matrix should be a symmetric 2 x 2 matrix, got [1] + sage: R = lie_conformal_algebras.FreeBosons(QQbar, ngens=2, gram_matrix=Matrix(QQ,[[0,1],[-1,0]])) + Traceback (most recent call last): + ... + ValueError: the gram_matrix should be a symmetric 2 x 2 matrix, got [ 0 1] + [-1 0] + """ + def __init__(self, R, ngens=None, gram_matrix=None, names=None, + index_set=None): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.FreeBosons(QQ) + sage: TestSuite(V).run() + """ + from sage.matrix.matrix_space import MatrixSpace + if (gram_matrix is not None): + if ngens is None: + ngens = gram_matrix.dimensions()[0] + try: + assert (gram_matrix in MatrixSpace(R,ngens,ngens)) + except AssertionError: + raise ValueError("the gram_matrix should be a symmetric " + + "{0} x {0} matrix, got {1}".format(ngens,gram_matrix)) + if not gram_matrix.is_symmetric(): + raise ValueError("the gram_matrix should be a symmetric " + + "{0} x {0} matrix, got {1}".format(ngens,gram_matrix)) + else: + if ngens is None: + ngens = 1; + gram_matrix = identity_matrix(R,ngens,ngens) + + latex_names = None + if (names is None) and (index_set is None): + names = 'alpha' + latex_names = tuple(r'\alpha_{%d}' % i \ + for i in range (ngens)) + ('K',) + names,index_set = standardize_names_index_set(names=names, + index_set=index_set, + ngens=ngens) + bosondict = { (i,j): {1: {('K',0): gram_matrix[index_set.rank(i), + index_set.rank(j)]}} for i in index_set for j in index_set} + + GradedLieConformalAlgebra.__init__(self,R,bosondict,names=names, + latex_names=latex_names, + index_set=index_set, + central_elements=('K',)) + + self._gram_matrix = gram_matrix + + def _repr_(self): + """ + String representation. + + EXAMPLES:: + + sage: lie_conformal_algebras.FreeBosons(AA) + The free Bosons Lie conformal algebra with generators (alpha, K) over Algebraic Real Field + """ + return "The free Bosons Lie conformal algebra with generators {}"\ + " over {}".format(self.gens(),self.base_ring()) + + def gram_matrix(self): + r""" + The Gram matrix that specifies the `\lambda`-brackets of the + generators. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.FreeBosons(QQ,ngens=2); + sage: R.gram_matrix() + [1 0] + [0 1] + """ + return self._gram_matrix + diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py new file mode 100644 index 00000000000..f74f3cf9b79 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -0,0 +1,159 @@ +r""" +Free Fermions Super Lie Conformal Algebra. + +Given an `R`-module `M` with a skew-symmetric, bilinear pairing +`\langle\cdot, \cdot\rangle: M\otimes_R M \rightarrow R`. The +*Free Fermions* super Lie conformal algebra associated to this datum is +the free `R[T]`-super module generated by `\Pi M` (a purely odd copy +of `M`) plus a central vector `K` satisfying `TK=0`. The remaining +`\lambda`-brackets are given by: + +.. MATH:: + + [v_\lambda w] = \langle v,w \rangle K, + +where `v,w \in M`. + +This is an H-graded Lie conformal algebra where every generator +`v \in M` has degree `1/2`. + + +AUTHORS: + +- Reimundo Heluani (2020-06-03): Initial implementation. +""" +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra + +class FreeFermionsLieConformalAlgebra(GradedLieConformalAlgebra): + r""" + The Free Fermions Super Lie conformal algebra. + + INPUT: + + - ``R``: a commutative ring. + - ``ngens``: a positive Integer (default ``1``); the number of + non-central generators of this Lie conformal algebra. + - ``gram_matrix``: a symmetric square matrix with coefficients + in ``R`` (default: ``identity_matrix(ngens)``); the Gram + matrix of the inner product + + OUTPUT: + + The Free Fermions Lie conformal algebra with generators + `\psi_i`, `i=1,...,n` and `\lambda`-brackets + + .. MATH:: + + [{\psi_i}_{\lambda} \psi_j] = M_{ij} K, + + where `n` is the number of generators ``ngens`` and `M` is the + ``gram_matrix``. This super Lie conformal + algebra is `H`-graded where every generator has degree `1/2`. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.FreeFermions(QQbar); R + The free Fermions super Lie conformal algebra with generators (psi, K) over Algebraic Field. + sage: R.inject_variables() + Defining psi, K + sage: psi.bracket(psi) + {0: K} + + sage: R = lie_conformal_algebras.FreeFermions(QQbar,gram_matrix=Matrix([[0,1],[1,0]])); R + The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field. + sage: R.inject_variables() + Defining psi_0, psi_1, K + sage: psi_0.bracket(psi_1) + {0: K} + sage: psi_0.degree() + 1/2 + sage: R.category() + Category of finitely generated super H-graded Lie conformal algebras with basis over Algebraic Field + """ + def __init__(self, R, ngens=None, gram_matrix=None, names=None, + index_set=None): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.FreeFermions(QQ) + sage: TestSuite(V).run() + """ + from sage.matrix.matrix_space import MatrixSpace + from sage.matrix.special import identity_matrix + if (gram_matrix is not None): + if ngens is None: + ngens = gram_matrix.dimensions()[0] + try: + assert (gram_matrix in MatrixSpace(R,ngens,ngens)) + except AssertionError: + raise ValueError("The gram_matrix should be a symmetric " + + "{0} x {0} matrix, got {1}".format(ngens,gram_matrix)) + if not gram_matrix.is_symmetric(): + raise ValueError("The gram_matrix should be a symmetric " + + "{0} x {0} matrix, got {1}".format(ngens,gram_matrix)) + else: + if ngens is None: + ngens = 1; + gram_matrix = identity_matrix(R,ngens,ngens) + + latex_names=None + + if (names is None) and (index_set is None): + if ngens==1: + names = 'psi' + else: + names = 'psi_' + latex_names = tuple(r"\psi_{%d}" % i \ + for i in range (ngens)) + ('K',) + + from sage.structure.indexed_generators import \ + standardize_names_index_set + names,index_set = standardize_names_index_set(names=names, + index_set=index_set, + ngens=ngens) + fermiondict = { (i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + index_set.rank(j)]}} for i in index_set for j in index_set} + + from sage.rings.rational_field import QQ + weights = (QQ(1/2),)*ngens + parity = (1,)*ngens + GradedLieConformalAlgebra.__init__(self,R,fermiondict,names=names, + latex_names=latex_names, + index_set=index_set,weights=weights, + parity=parity, + central_elements=('K',)) + + self._gram_matrix = gram_matrix + + def _repr_(self): + return "The free Fermions super Lie conformal algebra "\ + "with generators {} over {}.".format(self.gens(), + self.base_ring()) + + def gram_matrix(self): + r""" + The Gram matrix that specifies the `\lambda`-brackets of the + generators. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.FreeFermions(QQ,ngens=2); + sage: R.gram_matrix() + [1 0] + [0 1] + """ + return self._gram_matrix + + diff --git a/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py new file mode 100644 index 00000000000..5ee696bee17 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py @@ -0,0 +1,114 @@ +r""" +N=2 Super Lie Conformal Algebra + +The `N=2` super Lie conformal algebra is an extension of the Virasoro +Lie conformal algebra (with generators `L,C`) by an even generator `J` +which is primary of conformal weight `1` and two odd generators +`G_1,G_2` which are primary of conformal weight `3/2`. The remaining +`\lambda`-brackets are given by: + +.. MATH:: + + [J_\lambda J] &= \frac{\lambda}{3} C, \\ + [J_\lambda G_1] &= G_1, \\ + [J_\lambda G_2] &= -G_2, \\ + [{G_1}_\lambda G_1] &= [{G_2}_\lambda G_2 ] = 0, \\ + [{G_1}_\lambda G_2] &= L + \frac{1}{2} TJ + \lambda J + \frac{\lambda^2}{6}C. + +AUTHORS: + +- Reimundo Heluani (2020-06-03): Initial implementation. +""" +#****************************************************************************** +# Copyright (C) 2020 Reimundo Heluani +# +# 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 .graded_lie_conformal_algebra import GradedLieConformalAlgebra + +class N2LieConformalAlgebra(GradedLieConformalAlgebra): + """ + The N=2 super Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring; the base ring of this super + Lie conformal algebra. + + EXAMPLES:: + + sage: F. = NumberField(x^2 -2) + sage: R = lie_conformal_algebras.N2(F); R + The N=2 super Lie conformal algebra over Number Field in x with defining polynomial x^2 - 2 + sage: R.inject_variables() + Defining L, J, G1, G2, C + sage: G1.bracket(G2) + {0: L + 1/2*TJ, 1: J, 2: 1/3*C} + sage: G2.bracket(G1) + {0: L - 1/2*TJ, 1: -J, 2: 1/3*C} + sage: G1.degree() + 3/2 + sage: J.degree() + 1 + + The topological twist is a Virasoro vector with central + charge 0:: + + sage: L2 = L - 1/2*J.T() + sage: L2.bracket(L2) == {0: L2.T(), 1: 2*L2} + True + + The sum of the fermions is a generator of the Neveu-Schwarz + Lie conformal algebra:: + + sage: G = (G1 + G2) + sage: G.bracket(G) + {0: 2*L, 2: 2/3*C} + """ + def __init__(self,R): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.N2(QQ) + sage: TestSuite(V).run() + """ + n2dict =\ + {('L','L'):{0:{('L',1):1}, 1:{('L',0): 2}, + 3:{('C', 0):R(2).inverse_of_unit()}}, + ('L','G1'):{0:{('G1',1):1}, 1:{('G1',0):3*R(2).\ + inverse_of_unit()}}, + ('L','G2'):{0:{('G2',1):1}, 1:{('G2',0):3*R(2).\ + inverse_of_unit()}}, + ('G1','G2'): {0:{('L',0):1,('J',1):R(2).inverse_of_unit()}, + 1:{('J',0):1}, 2:{('C',0):R(3).inverse_of_unit()}}, + ('L','J'): {0:{('J',1):1},1:{('J',0):1}}, + ('J','J'): {1:{('C',0):R(3).inverse_of_unit()}}, + ('J','G1'): {0:{('G1',0):1}}, + ('J','G2'): {0:{('G2',0):-1}}} + from sage.rings.rational_field import QQ + weights = (2,1,QQ(3/2),QQ(3/2)) + parity = (0,0,1,1) + GradedLieConformalAlgebra.__init__(self,R,n2dict, + names=('L', 'J','G1','G2'), + central_elements=('C',), + weights=weights, parity=parity) + + def _repr_(self): + """ + The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.N2(QQbar); R + The N=2 super Lie conformal algebra over Algebraic Field + + """ + return "The N=2 super Lie conformal algebra over {}".\ + format(self.base_ring()) diff --git a/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py new file mode 100644 index 00000000000..0ce096864a8 --- /dev/null +++ b/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py @@ -0,0 +1,206 @@ +r""" +Weyl Lie Conformal Algebra + +Given a commutative ring `R`, a free `R`-module `M` and a +non-degenerate, skew-symmetric, bilinear pairing +`\langle \cdot,\cdot\rangle: M \otimes_R M \rightarrow R`. The *Weyl* +Lie conformal algebra associated to this datum is the free +`R[T]`-module generated by `M` plus a central vector `K`. The +non-vanishing `\lambda`-brackets are given by: + +.. MATH:: + + [v_\lambda w] = \langle v, w\rangle K. + +This is not an H-graded Lie conformal algebra. The choice of a +Lagrangian decomposition `M = L \oplus L^*` determines an H-graded +structure. For this H-graded Lie conformal algebra see the +:mod:`Bosonic Ghosts Lie conformal algebra` + +AUTHORS: + +- Reimundo Heluani (2019-08-09): Initial implementation. +""" + +#****************************************************************************** +# Copyright (C) 2019 Reimundo Heluani +# +# 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 .lie_conformal_algebra_with_structure_coefs import \ + LieConformalAlgebraWithStructureCoefficients +from sage.matrix.special import identity_matrix +from sage.structure.indexed_generators import standardize_names_index_set + +class WeylLieConformalAlgebra(LieConformalAlgebraWithStructureCoefficients): + r""" + The Weyl Lie conformal algebra. + + INPUT: + + - ``R`` -- a commutative ring; the base ring of this Lie + conformal algebra. + - ``ngens``: an even positive Integer (default `2`); The number + of non-central generators of this Lie conformal algebra. + - ``gram_matrix``: a matrix (default: ``None``); A non-singular + skew-symmetric square matrix with coefficients in `R`. + - ``names`` -- a list or tuple of ``str``; alternative names + for the generators + - ``index_set`` -- an enumerated set; alternative indexing set + for the generators + + + OUTPUT: + + The Weyl Lie conformal algebra with generators + `\alpha_i`, `i=1,...,ngens` and `\lambda`-brackets + + .. MATH:: + + [{\alpha_i}_{\lambda} \alpha_j] = M_{ij} K, + + where `M` is the ``gram_matrix`` above. + + .. NOTE:: + + The returned Lie conformal algebra is not `H`-graded. For + a related `H`-graded Lie conformal algebra see + :class:`BosonicGhostsLieConformalAlgebra`. + + EXAMPLES:: + + sage: lie_conformal_algebras.Weyl(QQ) + The Weyl Lie conformal algebra with generators (alpha0, alpha1, K) over Rational Field + sage: R = lie_conformal_algebras.Weyl(QQbar, gram_matrix=Matrix(QQ,[[0,1],[-1,0]]), names = ('a','b')) + sage: R.inject_variables() + Defining a, b, K + sage: a.bracket(b) + {0: K} + sage: b.bracket(a) + {0: -K} + + sage: R = lie_conformal_algebras.Weyl(QQbar, ngens=4) + sage: R.gram_matrix() + [ 0 0| 1 0] + [ 0 0| 0 1] + [-----+-----] + [-1 0| 0 0] + [ 0 -1| 0 0] + sage: R.inject_variables() + Defining alpha0, alpha1, alpha2, alpha3, K + sage: alpha0.bracket(alpha2) + {0: K} + + sage: R = lie_conformal_algebras.Weyl(QQ); R.category() + Category of finitely generated Lie conformal algebras with basis over Rational Field + sage: R.is_graded() + False + sage: R.inject_variables() + Defining alpha0, alpha1, K + sage: alpha0.degree() + Traceback (most recent call last): + ... + AttributeError: 'WeylLieConformalAlgebra_with_category.element_class' object has no attribute 'degree' + + TESTS:: + + sage: lie_conformal_algebras.Weyl(ZZ, gram_matrix=identity_matrix(ZZ,3)) + Traceback (most recent call last): + ... + ValueError: The gram_matrix should be a non degenerate skew-symmetric 3 x 3 matrix, got [1 0 0] + [0 1 0] + [0 0 1] + """ + def __init__(self,R,ngens=None, gram_matrix=None, names=None, + index_set=None): + """ + Initialize self. + + TESTS:: + + sage: V = lie_conformal_algebras.Weyl(QQ) + sage: TestSuite(V).run() + """ + from sage.matrix.matrix_space import MatrixSpace + if ngens: + try: + from sage.rings.all import ZZ + assert ngens in ZZ and ngens % 2 == 0 + except AssertionError: + raise ValueError("ngens needs to be an even positive "+ + "Integer, got {}".format(ngens)) + if (gram_matrix is not None): + if ngens is None: + ngens = gram_matrix.dimensions()[0] + try: + assert (gram_matrix in MatrixSpace(R,ngens,ngens)) + except AssertionError: + raise ValueError("The gram_matrix should be a skew-symmetric "+ + "{0} x {0} matrix, got {1}".format(ngens,gram_matrix)) + if (not gram_matrix.is_skew_symmetric()) or \ + (gram_matrix.is_singular()): + raise ValueError("The gram_matrix should be a non degenerate " + + "skew-symmetric {0} x {0} matrix, got {1}"\ + .format(ngens,gram_matrix)) + elif (gram_matrix is None): + if ngens is None: + ngens = 2; + A = identity_matrix(R,ngens/2) + from sage.matrix.special import block_matrix + gram_matrix = block_matrix([[R.zero(),A],[-A,R.zero()]]) + + latex_names = None + if (names is None) and (index_set is None): + names = 'alpha' + latex_names = tuple(r'\alpha_{%d}' % i \ + for i in range (ngens)) + ('K',) + names,index_set = standardize_names_index_set(names=names, + index_set=index_set, + ngens=ngens) + weyldict = { (i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + index_set.rank(j)]}} for i in index_set for j in index_set} + + super(WeylLieConformalAlgebra,self).__init__(R,weyldict,names=names, + latex_names=latex_names, + index_set=index_set, + central_elements=('K',)) + self._gram_matrix = gram_matrix + + def _repr_(self): + """ + The name of this Lie conformal algebra. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.Weyl(ZZ); R + The Weyl Lie conformal algebra with generators (alpha0, alpha1, K) over Integer Ring + """ + return "The Weyl Lie conformal algebra with generators {} over {}"\ + .format(self.gens(),self.base_ring()) + + def gram_matrix(self): + r""" + The Gram matrix that specifies the `\lambda`-brackets of the + generators. + + EXAMPLES:: + + sage: R = lie_conformal_algebras.Weyl(QQbar, ngens=4) + sage: R.gram_matrix() + [ 0 0| 1 0] + [ 0 0| 0 1] + [-----+-----] + [-1 0| 0 0] + [ 0 -1| 0 0] + """ + return self._gram_matrix + + From 6bea714649ad1e3ce7e5de8916854a8a9b440a9f Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Thu, 2 Jul 2020 07:28:07 -0300 Subject: [PATCH 344/379] Added missing docstrings --- .../free_fermions_lie_conformal_algebra.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index f74f3cf9b79..b0b6346c2e6 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -63,14 +63,14 @@ class FreeFermionsLieConformalAlgebra(GradedLieConformalAlgebra): EXAMPLES:: sage: R = lie_conformal_algebras.FreeFermions(QQbar); R - The free Fermions super Lie conformal algebra with generators (psi, K) over Algebraic Field. + The free Fermions super Lie conformal algebra with generators (psi, K) over Algebraic Field sage: R.inject_variables() Defining psi, K sage: psi.bracket(psi) {0: K} sage: R = lie_conformal_algebras.FreeFermions(QQbar,gram_matrix=Matrix([[0,1],[1,0]])); R - The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field. + The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field sage: R.inject_variables() Defining psi_0, psi_1, K sage: psi_0.bracket(psi_1) @@ -138,8 +138,16 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, self._gram_matrix = gram_matrix def _repr_(self): + """ + String representation. + + EXAMPLES:: + + sage: lie_conformal_algebras.FreeFermions(QQ) + The free Fermions super Lie conformal algebra with generators (psi, K) over Rational Field + """ return "The free Fermions super Lie conformal algebra "\ - "with generators {} over {}.".format(self.gens(), + "with generators {} over {}".format(self.gens(), self.base_ring()) def gram_matrix(self): From 0974baf645910925e12af8b05cf83d2d2a5a4bb5 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Wed, 1 Jul 2020 22:14:02 -0300 Subject: [PATCH 345/379] Implemented some examples of Lie conformal algebras --- .../free_fermions_lie_conformal_algebra.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index b0b6346c2e6..9391914b13e 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -71,6 +71,7 @@ class FreeFermionsLieConformalAlgebra(GradedLieConformalAlgebra): sage: R = lie_conformal_algebras.FreeFermions(QQbar,gram_matrix=Matrix([[0,1],[1,0]])); R The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field + The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field. sage: R.inject_variables() Defining psi_0, psi_1, K sage: psi_0.bracket(psi_1) From e278983b43be0295f020380f0c02c6177a0c28cb Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Thu, 2 Jul 2020 07:28:07 -0300 Subject: [PATCH 346/379] Added missing docstrings --- .../free_fermions_lie_conformal_algebra.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index 9391914b13e..b0b6346c2e6 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -71,7 +71,6 @@ class FreeFermionsLieConformalAlgebra(GradedLieConformalAlgebra): sage: R = lie_conformal_algebras.FreeFermions(QQbar,gram_matrix=Matrix([[0,1],[1,0]])); R The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field - The free Fermions super Lie conformal algebra with generators (psi_0, psi_1, K) over Algebraic Field. sage: R.inject_variables() Defining psi_0, psi_1, K sage: psi_0.bracket(psi_1) From 534cd9c5c2be1ee27356ff3f06da4b35f7e04a0d Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sun, 5 Jul 2020 13:15:04 -0300 Subject: [PATCH 347/379] rebased onto #30032 and fixed _test_jacobi's corner case. --- .../bosonic_ghosts_lie_conformal_algebra.py | 2 +- .../fermionic_ghosts_lie_conformal_algebra.py | 2 +- .../free_fermions_lie_conformal_algebra.py | 2 +- .../lie_conformal_algebras/weyl_lie_conformal_algebra.py | 2 +- .../categories/finitely_generated_lie_conformal_algebras.py | 2 ++ src/sage/categories/super_lie_conformal_algebras.py | 4 +++- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py index 9fce5572cdd..47a7bb15823 100644 --- a/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py @@ -73,7 +73,7 @@ class BosonicGhostsLieConformalAlgebra(GradedLieConformalAlgebra): TESTS:: sage: lie_conformal_algebras.BosonicGhosts(AA).category() - Category of finitely generated H-graded Lie conformal algebras with basis over Algebraic Real Field + Category of H-graded finitely generated Lie conformal algebras with basis over Algebraic Real Field """ def __init__(self, R, ngens=2, names=None, index_set=None): diff --git a/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py index 11421cfdbd2..1615ae1bdca 100644 --- a/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py @@ -63,7 +63,7 @@ class FermionicGhostsLieConformalAlgebra(GradedLieConformalAlgebra): sage: c.degree() 0 sage: R.category() - Category of finitely generated super H-graded Lie conformal algebras with basis over Rational Field + Category of H-graded super finitely generated Lie conformal algebras with basis over Rational Field sage: R = lie_conformal_algebras.FermionicGhosts(QQbar, ngens=4, names = 'abcd');R The Fermionic ghosts Lie conformal algebra with generators (a, b, c, d, K) over Algebraic Field diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index b0b6346c2e6..6d7303dac17 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -78,7 +78,7 @@ class FreeFermionsLieConformalAlgebra(GradedLieConformalAlgebra): sage: psi_0.degree() 1/2 sage: R.category() - Category of finitely generated super H-graded Lie conformal algebras with basis over Algebraic Field + Category of H-graded super finitely generated Lie conformal algebras with basis over Algebraic Field """ def __init__(self, R, ngens=None, gram_matrix=None, names=None, index_set=None): diff --git a/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py index 0ce096864a8..68e0841ec85 100644 --- a/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py @@ -101,7 +101,7 @@ class WeylLieConformalAlgebra(LieConformalAlgebraWithStructureCoefficients): sage: R = lie_conformal_algebras.Weyl(QQ); R.category() Category of finitely generated Lie conformal algebras with basis over Rational Field - sage: R.is_graded() + sage: R in LieConformalAlgebras(QQ).Graded() False sage: R.inject_variables() Defining alpha0, alpha1, K diff --git a/src/sage/categories/finitely_generated_lie_conformal_algebras.py b/src/sage/categories/finitely_generated_lie_conformal_algebras.py index 0dc5bc5cddc..2422a2f5210 100644 --- a/src/sage/categories/finitely_generated_lie_conformal_algebras.py +++ b/src/sage/categories/finitely_generated_lie_conformal_algebras.py @@ -32,6 +32,8 @@ class FinitelyGeneratedLieConformalAlgebras(CategoryWithAxiom_over_base_ring): """ _base_category_class_and_axiom = (LieConformalAlgebras, "FinitelyGeneratedAsLambdaBracketAlgebra") + class ParentMethods: + def some_elements(self): """ Some elements of this Lie conformal algebra. diff --git a/src/sage/categories/super_lie_conformal_algebras.py b/src/sage/categories/super_lie_conformal_algebras.py index 4ed3d69f709..94ea11ece52 100644 --- a/src/sage/categories/super_lie_conformal_algebras.py +++ b/src/sage/categories/super_lie_conformal_algebras.py @@ -125,7 +125,9 @@ def _test_jacobi(self, **options): from sage.functions.other import binomial pz = tester._instance.zero() for x,y,z in some_tuples(S, 3, tester._max_runs): - if x.is_even_odd() * y.is_even_odd(): + if x.is_zero() or y.is_zero(): + sgn = 1 + elif x.is_even_odd() * y.is_even_odd(): sgn = -1 else: sgn = 1 From 8dcf1b2f1cdf27c1350e11a9e6844abbade87da6 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 24 Aug 2020 16:06:25 -0400 Subject: [PATCH 348/379] 29844: added ideal method for element class --- .../schemes/berkovich/berkovich_cp_element.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 1a1a14afa32..6a315835850 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -519,9 +519,30 @@ def precision(self): prec = precision + def ideal(self): + r""" + The ideal which defines an embedding of the ``base_ring`` into `\CC_p`. + + If this Berkovich space is backed by a p-adic field, then an embedding is + already specified, and this returns ``None``. + + EXAMPLES:: + + sage: B = Berkovich_Cp_Projective(QQ, 3) + sage: B(0).ideal() + 3 + + :: + + sage: B = Berkovich_Cp_Projective(3) + sage: B(0).ideal() + + """ + return self.parent().ideal() + def power(self): r""" - Power of ``p`` such that `p^\text{power} = \text{radius}`. + The power of ``p`` such that `p^\text{power} = \text{radius}`. For type II points, always in `\QQ`. For type III points, a real number. Not defined for type I or IV points. From a80dcbe55593852502b28e627e28014b0366e5b2 Mon Sep 17 00:00:00 2001 From: Stefan Grosser Date: Mon, 24 Aug 2020 20:13:16 -0400 Subject: [PATCH 349/379] modified documentation. removed multislant sum. declared the empty poset as not a mobile --- src/sage/combinat/posets/d_complete.py | 14 +- src/sage/combinat/posets/linear_extensions.py | 18 ++- src/sage/combinat/posets/mobile.py | 135 ++++++++++-------- src/sage/combinat/posets/posets.py | 55 +++---- 4 files changed, 115 insertions(+), 107 deletions(-) diff --git a/src/sage/combinat/posets/d_complete.py b/src/sage/combinat/posets/d_complete.py index dd1e6cfc3c6..5ac24705798 100644 --- a/src/sage/combinat/posets/d_complete.py +++ b/src/sage/combinat/posets/d_complete.py @@ -158,8 +158,18 @@ def get_hooks(self): def hook_product(self): r""" Return the hook product for the poset. + + TESTS:: + + sage: from sage.combinat.posets.d_complete import DCompletePoset + sage: P = DCompletePoset(DiGraph({0: [1, 2], 1: [3], 2: [3], 3: []})) + sage: P.hook_product() + 12 + sage: P = DCompletePoset(posets.YoungDiagramPoset(Partition([3,2,1]), dual=True)) + sage: P.hook_product() + 45 """ - if self.cardinality() == 0: - return ZZ(1) + if not self._hasse_diagram: + return ZZ.one() return ZZ(prod(self._hooks.values())) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index ed630aa6894..547c22f4660 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -932,14 +932,14 @@ def cardinality(self): fold_down = [] for ind, r in enumerate(self._poset._ribbon[:-1]): - if ind < anchor_index and self._poset.is_greater_than(r, self._poset._ribbon[ind+1]): + if ind < anchor_index and self._poset.is_greater_than(r, self._poset._ribbon[ind + 1]): fold_up.append((self._poset._ribbon[ind + 1], r)) - elif ind >= anchor_index and self._poset.is_less_than(r, self._poset._ribbon[ind+1]): - fold_down.append((r, self._poset._ribbon[ind+1])) + elif ind >= anchor_index and self._poset.is_less_than(r, self._poset._ribbon[ind + 1]): + fold_down.append((r, self._poset._ribbon[ind + 1])) folds = fold_up + fold_down - if len(folds) == 0: + if not folds: return dc.DCompletePoset(self._poset).linear_extensions().cardinality() # Get ordered connected components @@ -951,10 +951,14 @@ def cardinality(self): ordered_poset_components = [poset_components.connected_component_containing_vertex(l, sort=False) for l in [fold[1] for fold in fold_up] + [fold[0] for fold in fold_down]] ordered_poset_components.append(poset_components.connected_component_containing_vertex( - folds[-1][1] if len(fold_down) > 0 else folds[-1][0] - , sort=False)) + fold_down[-1][1] if len(fold_down) > 0 else fold_up[-1][0], sort=False)) # Return determinant + + # Remove extra list + folds = fold_up + folds.extend(fold_down) + mat = [] for i in range(len(folds)+1): mat_poset = dc.DCompletePoset(self._poset.subposet(ordered_poset_components[i])) @@ -962,7 +966,7 @@ def cardinality(self): row.append(1 / mat_poset.hook_product()) for j, f in enumerate(folds[i:]): next_poset = self._poset.subposet(ordered_poset_components[j+i+1]) - mat_poset = dc.DCompletePoset(fp.FinitePoset.slant_sum(mat_poset, next_poset, f[1], f[0])) + mat_poset = dc.DCompletePoset(next_poset.slant_sum(mat_poset, f[0], f[1])) row.append(1 / mat_poset.hook_product()) mat.append(row) diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index 7333679829e..b4f35f6fcf9 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -22,56 +22,72 @@ class MobilePoset(FinitePoset): poset with d-complete posets 'hanging' below it and at most one d-complete poset above it, known as the anchor. See [GGMM2020]_ for the definition. + + EXAMPLES:: + + sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), {1: + ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, + ....: anchor=(4, 2, posets.ChainPoset(6))) + sage: len(P._ribbon) + 8 + sage: P._anchor + (4, 5) + + This example comes from Example 5.9 in [GGMM2020]_:: + + sage: P1 = posets.MobilePoset(posets.RibbonPoset(8, [2,3,4]), + ....: {4: [posets.ChainPoset(1)]}, anchor=(3, 0, posets.ChainPoset(1))) + sage: sorted([P1._element_to_vertex(i) for i in P1._ribbon]) + [0, 1, 2, 6, 7, 9] + sage: P1._anchor + (3, 2) + + sage: P2 = posets.MobilePoset(posets.RibbonPoset(15, [1,3,5,7,9,11,13]), + ....: {}, anchor=(8, 0, posets.ChainPoset(1))) + sage: sorted(P2._ribbon) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + sage: P2._anchor + (8, (8, 0)) + sage: P2.linear_extensions().cardinality() + 21399440939 + + sage: from sage.combinat.posets.mobile import MobilePoset + sage: EP = MobilePoset(posets.ChainPoset(0)) + Traceback (most recent call last): + ... + ValueError: Empty poset is not a mobile """ _lin_ext_type = LinearExtensionsOfMobile _desc = 'Finite mobile poset' - - def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None): + def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None, check=True): r""" - Initialize self. The second example comes from Example 5.9 in [GGMM2020]_ + Initialize self. EXAMPLES:: - sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), {1: - ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, - ....: anchor=(4, 2, posets.ChainPoset(6))) - sage: len(P._ribbon) - 8 - sage: P._anchor - (4, 5) - - sage: P1 = posets.MobilePoset(posets.RibbonPoset(8, [2,3,4]), - ....: {4: [posets.ChainPoset(1)]}, anchor=(3, 0, posets.ChainPoset(1))) - sage: sorted([P1._element_to_vertex(i) for i in P1._ribbon]) - [0, 1, 2, 6, 7, 9] - sage: P1._anchor - (3, 2) - - sage: P2 = posets.MobilePoset(posets.RibbonPoset(15, [1,3,5,7,9,11,13]), + sage: P = posets.MobilePoset(posets.RibbonPoset(15, [1,3,5,7,9,11,13]), ....: {}, anchor=(8, 0, posets.ChainPoset(1))) - sage: sorted(P2._ribbon) - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] - sage: P2._anchor - (8, (8, 0)) - sage: P2.linear_extensions().cardinality() - 21399440939 - - sage: TestSuite(P2).run() + sage: TestSuite(P).run() """ FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, category=category, facade=facade, key=key) - if ribbon and self._is_valid_ribbon(ribbon): + if not self._hasse_diagram: + raise ValueError('Empty poset is not a mobile') + + if ribbon: + if check and not self._is_valid_ribbon(ribbon): + raise ValueError('Invalid ribbon') self._ribbon = ribbon def _is_valid_ribbon(self, ribbon): r""" - Return True if a ribbon has at most one anchor, no vertex has two or more anchors, + Return ``True`` if a ribbon has at most one anchor, no vertex has two or more anchors, and every hanging poset is d-complete. INPUT: - - ``ribbon`` -- a list of elements that form a ribbon in your poset + - ``ribbon`` -- a list of elements that form a ribbon in your poset TESTS:: @@ -110,7 +126,6 @@ def _is_valid_ribbon(self, ribbon): return True - @lazy_attribute def _anchor(self): r""" @@ -123,13 +138,6 @@ def _anchor(self): ....: [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: M._anchor (4, 3) - sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], - ....: [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) - sage: M2._anchor - (4, 3) - sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) - sage: M3._anchor is None - True """ ribbon = list(map(lambda x: self._element_to_vertex(x), self._ribbon)) H = self._hasse_diagram @@ -145,8 +153,6 @@ def _anchor(self): break return (self._vertex_to_element(anchor[0]), self._vertex_to_element(anchor[1])) if not anchor is None else None - - @lazy_attribute def _ribbon(self): r""" @@ -165,17 +171,6 @@ def _ribbon(self): [4, 7, 8] sage: M2._is_valid_ribbon(M2._ribbon) True - sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) - sage: sorted(M3._ribbon) - [1, 2, 3, 4] - sage: M3._is_valid_ribbon(M3._ribbon) - True - """ - return [self._vertex_to_element(x) for x in self._compute_ribbon()] - - def _compute_ribbon(self): - r""" - The helper function of _ribbon for computing the ribbon. """ H = self._hasse_diagram H_un = H.to_undirected() @@ -184,7 +179,7 @@ def _compute_ribbon(self): ribbon = [] # In order list of elements on zigzag if len(max_elmts) == 1: - return [max_elmts[0]] + return [self._vertex_to_element(max_elmts[0])] # Compute max element tree by merging shortest paths start = max_elmts[0] @@ -205,8 +200,8 @@ def _compute_ribbon(self): traverse_ribbon = ribbon if end_count == 0 else ribbon[::-1] for ind, p in enumerate(traverse_ribbon): if H_un.is_cut_edge(p, traverse_ribbon[ind+1]): - return G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1]) - return ribbon + return [self._vertex_to_element(r) for r in G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1])] + return [self._vertex_to_element(r) for r in ribbon] # First check path counts between ends and deg3 vertex # Then check if more than one max elmt on way to degree 3 vertex. @@ -224,7 +219,7 @@ def _compute_ribbon(self): if anchoredEnd is not None: ends.remove(anchoredEnd) - return G.shortest_path(ends[0], ends[1]) + return [self._vertex_to_element(r) for r in G.shortest_path(ends[0], ends[1])] possible_anchors = ends[:] for end in ends: @@ -239,16 +234,36 @@ def _compute_ribbon(self): anchoredEnd = possible_anchors[0] ends.remove(anchoredEnd) - return G.shortest_path(ends[0], ends[1]) + return [self._vertex_to_element(r) for r in G.shortest_path(ends[0], ends[1])] - def get_ribbon(self): + def ribbon(self): r""" - Return the ribbon of the mobile poset + Return the ribbon of the mobile poset. + + TESTS:: + + sage: from sage.combinat.posets.mobile import MobilePoset + sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) + sage: sorted(M3.ribbon()) + [1, 2, 3, 4] + sage: M3._is_valid_ribbon(M3.ribbon()) + True """ return self._ribbon - def get_anchor(self): + def anchor(self): r""" - Return the anchor of the mobile poset + Return the anchor of the mobile poset. + + TESTS:: + + sage: from sage.combinat.posets.mobile import MobilePoset + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], + ....: [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: M2.anchor() + (4, 3) + sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) + sage: M3.anchor() is None + True """ return self._anchor \ No newline at end of file diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index d166193f715..f3f5a859318 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -2374,53 +2374,32 @@ def is_d_complete(self): return True - @staticmethod - def slant_sum(P1, P2, P1_element, P2_element): + def slant_sum(self, p, element, p_element): r""" - Return the slant sum poset of posets P1 and P2 by connecting them with a - cover relation (P1_element, P2_element). Element names of P1 and P2 must be distinct. + Return the slant sum poset of posets ``self`` and ``p`` by connecting them with a + cover relation ``(p_element, element)``. Element names of ``self`` and ``p`` must be distinct. INPUT: - - ``P1`` -- The first poset - - ``P2`` -- The second poset - - ``P1_element`` -- The element of P1 that is the bottom of the new cover relation - - ``P2_element`` -- The element of P2 that is the top of the new cover relation + - ``p`` -- The poset used for the slant sum + - ``element`` -- The element of ``self`` that is the top of the new cover relation + - ``p_element`` -- The element of ``p`` that is the bottom of the new cover relation EXAMPLES:: - sage: from sage.combinat.posets.posets import FinitePoset - sage: R = Posets.RibbonPoset(5, [1,2]) + sage: R = posets.RibbonPoset(5, [1,2]) sage: H = Poset([[5, 6, 7], [(5, 6), (6,7)]]) - sage: SS = FinitePoset.slant_sum(H, R, 7, 3) - sage: SS.cover_relations() - [[5, 6], [6, 7], [7, 3], [3, 4], [3, 2], [2, 1], [0, 1]] - """ - elements = P1._elements + P2._elements - cover_relations = P1.cover_relations() + P2.cover_relations() - cover_relations.append((P1_element, P2_element)) - return Poset([elements, cover_relations]) - - @staticmethod - def multislant_sum(psets, relations): - r""" - Return the union of posets provided in ``psets``, along with the additional cover relations - specified in ``relations`` - - INPUT: - - - ``psets`` -- A list of finite posets - - ``relations`` -- A list of new cover relations + sage: SS = R.slant_sum(H, 3, 7) + sage: all(cr in SS.cover_relations() for cr in R.cover_relations()) + True + sage: all(cr in SS.cover_relations() for cr in H.cover_relations()) + True + sage: SS.covers(7, 3) + True """ - elements = [] - cover_relations = [] - for p in psets: - elements.extend(p._elements) - cover_relations.extend(p.cover_relations()) - - for r in relations: - cover_relations.append(r) - + elements = p._elements + self._elements + cover_relations = p.cover_relations() + self.cover_relations() + cover_relations.append((p_element, element)) return Poset([elements, cover_relations]) def intervals_poset(self): From 310b121e1c6ae002128c3301203e0f760487c859 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 25 Aug 2020 10:21:01 +1000 Subject: [PATCH 350/379] Small tweaks to the cardinality method. --- src/sage/combinat/posets/linear_extensions.py | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 547c22f4660..e09732d24e8 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -928,36 +928,36 @@ def cardinality(self): else: anchor_index = len(self._poset._ribbon) - fold_up = [] - fold_down = [] + folds_up = [] + folds_down = [] for ind, r in enumerate(self._poset._ribbon[:-1]): if ind < anchor_index and self._poset.is_greater_than(r, self._poset._ribbon[ind + 1]): - fold_up.append((self._poset._ribbon[ind + 1], r)) + folds_up.append((self._poset._ribbon[ind + 1], r)) elif ind >= anchor_index and self._poset.is_less_than(r, self._poset._ribbon[ind + 1]): - fold_down.append((r, self._poset._ribbon[ind + 1])) + folds_down.append((r, self._poset._ribbon[ind + 1])) - folds = fold_up + fold_down - - if not folds: + if not folds_up and not folds_down: return dc.DCompletePoset(self._poset).linear_extensions().cardinality() # Get ordered connected components cr = self._poset.cover_relations() - foldless_cr = [tuple(c) for c in cr if tuple(c) not in folds] + foldless_cr = [tuple(c) for c in cr if tuple(c) not in folds_up and tuple(c) not in folds_down] elmts = list(self._poset._elements) poset_components = DiGraph([elmts, foldless_cr]) - ordered_poset_components = [poset_components.connected_component_containing_vertex(l, sort=False) - for l in [fold[1] for fold in fold_up] + [fold[0] for fold in fold_down]] + ordered_poset_components = [poset_components.connected_component_containing_vertex(f[1], sort=False) + for f in folds_up] + ordered_poset_components.extend(poset_components.connected_component_containing_vertex(f[0], sort=False) + for f in folds_down) ordered_poset_components.append(poset_components.connected_component_containing_vertex( - fold_down[-1][1] if len(fold_down) > 0 else fold_up[-1][0], sort=False)) + folds_down[-1][1] if folds_down else folds_up[-1][0], sort=False)) # Return determinant - # Remove extra list - folds = fold_up - folds.extend(fold_down) + # Consoludate the folds lists + folds = folds_up + folds.extend(folds_down) mat = [] for i in range(len(folds)+1): @@ -972,5 +972,3 @@ def cardinality(self): mat.append(row) return matrix(QQ, mat).determinant() * factorial(self._poset.cardinality()) - - From 084769e4bb132f2dfdc593cf0d57d74057c2ee6f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 25 Aug 2020 10:26:34 +1000 Subject: [PATCH 351/379] Reviewer changes for mobile.py. --- src/sage/combinat/posets/mobile.py | 59 ++++++++++++++++-------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index b4f35f6fcf9..6de93d54e58 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -25,25 +25,27 @@ class MobilePoset(FinitePoset): EXAMPLES:: - sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), {1: - ....: [posets.YoungDiagramPoset([3, 2], dual=True)], 3: [posets.DoubleTailedDiamond(6)]}, - ....: anchor=(4, 2, posets.ChainPoset(6))) + sage: P = posets.MobilePoset(posets.RibbonPoset(7, [1,3]), + ....: {1: [posets.YoungDiagramPoset([3, 2], dual=True)], + ....: 3: [posets.DoubleTailedDiamond(6)]}, + ....: anchor=(4, 2, posets.ChainPoset(6))) sage: len(P._ribbon) 8 sage: P._anchor (4, 5) - This example comes from Example 5.9 in [GGMM2020]_:: + This example is Example 5.9 in [GGMM2020]_:: sage: P1 = posets.MobilePoset(posets.RibbonPoset(8, [2,3,4]), - ....: {4: [posets.ChainPoset(1)]}, anchor=(3, 0, posets.ChainPoset(1))) + ....: {4: [posets.ChainPoset(1)]}, + ....: anchor=(3, 0, posets.ChainPoset(1))) sage: sorted([P1._element_to_vertex(i) for i in P1._ribbon]) [0, 1, 2, 6, 7, 9] sage: P1._anchor (3, 2) sage: P2 = posets.MobilePoset(posets.RibbonPoset(15, [1,3,5,7,9,11,13]), - ....: {}, anchor=(8, 0, posets.ChainPoset(1))) + ....: {}, anchor=(8, 0, posets.ChainPoset(1))) sage: sorted(P2._ribbon) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] sage: P2._anchor @@ -51,39 +53,38 @@ class MobilePoset(FinitePoset): sage: P2.linear_extensions().cardinality() 21399440939 - sage: from sage.combinat.posets.mobile import MobilePoset - sage: EP = MobilePoset(posets.ChainPoset(0)) + sage: EP = posets.MobilePoset(posets.ChainPoset(0), {}) Traceback (most recent call last): ... - ValueError: Empty poset is not a mobile + ValueError: the empty poset is not a mobile poset """ - _lin_ext_type = LinearExtensionsOfMobile _desc = 'Finite mobile poset' def __init__(self, hasse_diagram, elements, category, facade, key, ribbon=None, check=True): r""" - Initialize self. + Initialize ``self``. EXAMPLES:: sage: P = posets.MobilePoset(posets.RibbonPoset(15, [1,3,5,7,9,11,13]), - ....: {}, anchor=(8, 0, posets.ChainPoset(1))) + ....: {}, anchor=(8, 0, posets.ChainPoset(1))) sage: TestSuite(P).run() """ - FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, category=category, facade=facade, key=key) + FinitePoset.__init__(self, hasse_diagram=hasse_diagram, elements=elements, + category=category, facade=facade, key=key) if not self._hasse_diagram: - raise ValueError('Empty poset is not a mobile') + raise ValueError("the empty poset is not a mobile poset") if ribbon: if check and not self._is_valid_ribbon(ribbon): - raise ValueError('Invalid ribbon') + raise ValueError("invalid ribbon") self._ribbon = ribbon def _is_valid_ribbon(self, ribbon): r""" - Return ``True`` if a ribbon has at most one anchor, no vertex has two or more anchors, - and every hanging poset is d-complete. + Return ``True`` if a ribbon has at most one anchor, no vertex has two + or more anchors, and every hanging poset is d-complete. INPUT: @@ -135,7 +136,7 @@ def _anchor(self): sage: from sage.combinat.posets.mobile import MobilePoset sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], - ....: [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) + ....: [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: M._anchor (4, 3) """ @@ -161,12 +162,14 @@ def _ribbon(self): TESTS:: sage: from sage.combinat.posets.mobile import MobilePoset - sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) + sage: M = MobilePoset(DiGraph([[0,1,2,3,4,5,6,7,8], + ....: [(1,0),(3,0),(2,1),(2,3),(4,3), (5,4),(5,6),(7,4),(7,8)]])) sage: sorted(M._ribbon) [4, 5, 6, 7, 8] sage: M._is_valid_ribbon(M._ribbon) True - sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], + ....: [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) sage: sorted(M2._ribbon) [4, 7, 8] sage: M2._is_valid_ribbon(M2._ribbon) @@ -200,7 +203,8 @@ def _ribbon(self): traverse_ribbon = ribbon if end_count == 0 else ribbon[::-1] for ind, p in enumerate(traverse_ribbon): if H_un.is_cut_edge(p, traverse_ribbon[ind+1]): - return [self._vertex_to_element(r) for r in G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1])] + return [self._vertex_to_element(r) + for r in G.shortest_path(ends[(end_count + 1) % 2], traverse_ribbon[ind+1])] return [self._vertex_to_element(r) for r in ribbon] # First check path counts between ends and deg3 vertex @@ -224,7 +228,7 @@ def _ribbon(self): possible_anchors = ends[:] for end in ends: path = G.shortest_path(end, deg3) - if sum(map(lambda z: z in max_elmts, path)) != 1: + if sum(bool(z in max_elmts) for z in path) != 1: possible_anchors.remove(end) for p in possible_anchors: @@ -240,14 +244,12 @@ def ribbon(self): r""" Return the ribbon of the mobile poset. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.mobile import MobilePoset sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) sage: sorted(M3.ribbon()) [1, 2, 3, 4] - sage: M3._is_valid_ribbon(M3.ribbon()) - True """ return self._ribbon @@ -255,15 +257,16 @@ def anchor(self): r""" Return the anchor of the mobile poset. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.mobile import MobilePoset sage: M2 = MobilePoset(Poset([[0,1,2,3,4,5,6,7,8], - ....: [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) + ....: [(1,0),(3,0),(2,1),(2,3),(4,3),(5,4),(7,4),(7,8)]])) sage: M2.anchor() (4, 3) sage: M3 = MobilePoset(Posets.RibbonPoset(5, [1,2])) sage: M3.anchor() is None True """ - return self._anchor \ No newline at end of file + return self._anchor + From 3c3b6b0e31a290b68febdc822e614ddec3240f94 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 25 Aug 2020 10:30:02 +1000 Subject: [PATCH 352/379] Final reviewer changes for mobile posets. --- src/doc/en/reference/references/index.rst | 5 +++-- src/sage/combinat/posets/posets.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index fa4afd9ba69..f009f304f53 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2350,8 +2350,9 @@ REFERENCES: Riemann surfaces and Dessins d'enfant*, (2011) London Mathematical Society, Student Text 79. -.. [GGMM2020] A. Garver, S. Grosser, J. Matherne, A. Morales, Counting linear extensions of - posets with determinants of hook lengths, :arxiv:`2001.08822` +.. [GGMM2020] \A. Garver, S. Grosser, J. Matherne, and A. Morales. + *Counting linear extensions of posets with determinants of hook + lengths*. Preprint, :arxiv:`2001.08822` (2020). .. [GGNS2013] \B. Gerard, V. Grosso, M. Naya-Plasencia, and F.-X. Standaert, *Block ciphers that are easier to mask: How far can we go?*; in diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 9e819beff77..fd2a18274af 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -120,6 +120,7 @@ :meth:`~FinitePoset.random_subposet` | Return a random subposet that contains each element with given probability. :meth:`~FinitePoset.relabel` | Return a copy of this poset with its elements relabelled. :meth:`~FinitePoset.canonical_label` | Return copy of the poset canonically (re)labelled to integers. + :meth:`~FinitePoset.slant_sum` | Return the slant sum poset of two posets. **Chains & antichains** @@ -2376,14 +2377,20 @@ def is_d_complete(self): def slant_sum(self, p, element, p_element): r""" - Return the slant sum poset of posets ``self`` and ``p`` by connecting them with a - cover relation ``(p_element, element)``. Element names of ``self`` and ``p`` must be distinct. + Return the slant sum poset of posets ``self`` and ``p`` by + connecting them with a cover relation ``(p_element, element)``. + + .. NOTE:: + + The element names of ``self`` and ``p`` must be distinct. INPUT: - - ``p`` -- The poset used for the slant sum - - ``element`` -- The element of ``self`` that is the top of the new cover relation - - ``p_element`` -- The element of ``p`` that is the bottom of the new cover relation + - ``p`` -- the poset used for the slant sum + - ``element`` -- the element of ``self`` that is the top of + the new cover relation + - ``p_element`` -- the element of ``p`` that is the bottom of + the new cover relation EXAMPLES:: From 8aa546b55d4a5b7c5d130a9960e2b96038904b64 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 25 Aug 2020 10:33:57 +1000 Subject: [PATCH 353/379] Fixing minor thing with is_path() in graph.py. --- src/sage/graphs/graph.py | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 96d43d42eec..2e6ed86c044 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3236,9 +3236,10 @@ def is_path(self): r""" Check whether ``self`` is a path. - A connected graph of order `n \geq 2` is a path if it is a tree (see :meth:`is_tree`) - with `n-2` vertices of degree 2 and two of degree 1. - By convention, a graph of order 1 without loops is a path, but the empty graph is not a path. + A connected graph of order `n \geq 2` is a path if it is a tree + (see :meth:`is_tree`) with `n-2` vertices of degree 2 and two of + degree 1. By convention, a graph of order 1 without loops is a path, + but the empty graph is not a path. EXAMPLES: @@ -4814,23 +4815,6 @@ def maximum_average_degree(self, value_only=True, solver=None, verbose=0): else: return g_mad - @doc_index("Graph properties") - def is_path(self): - r""" - Return true if the graph is a path (has degree sequence of n-2 2's and two 1's). - - EXAMPLES: - - sage: G = graphs.PathGraph(5) - sage: G.is_path() - True - sage: H = graphs.CycleGraph(5) - sage: H.is_path() - False - """ - ds = self.degree_sequence() - return all(map(lambda x: x[0] == x[1], zip(ds, ([2] * (self.order() - 2) + ([1] * 2))))) - @doc_index("Algorithmically hard stuff") def independent_set_of_representatives(self, family, solver=None, verbose=0): r""" From a1ea948d7858e2e6d004ec898fd5dbcaf5e92791 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 25 Aug 2020 14:33:31 +1000 Subject: [PATCH 354/379] Addressing reviewer comments. Handling dim >= 10. --- ...ite_dimensional_lie_algebras_with_basis.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 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 9a8d8619f00..2fd27b6a212 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -1435,12 +1435,12 @@ def universal_polynomials(self): The *universal polynomials* of a Lie algebra `L` with basis `\{e_i\}_{i \in I}` and structure coefficients - `[e_i, e_j] = \tau_{ij}^a e_a is given by + `[e_i, e_j] = \tau_{ij}^a e_a` is given by .. MATH:: P_{aij} = \sum_{u \in I} \tau_{ij}^u X_{au} - - \sum_{s,t \in I} \tau_{st}^a X_{ai} X_{tj}, + - \sum_{s,t \in I} \tau_{st}^a X_{si} X_{tj}, where `a,i,j \in I`. @@ -1466,6 +1466,16 @@ def universal_polynomials(self): -2*X11*X20 + 2*X10*X21 - 2*X20, -2*X12*X20 + 2*X10*X22 + X21, -2*X12*X21 + 2*X11*X22 - 2*X22] + + sage: L = LieAlgebra(QQ, cartan_type=['B',2]) + sage: al = RootSystem(['B',2]).root_lattice().simple_roots() + sage: k = list(L.basis().keys())[0] + sage: UP = L.universal_polynomials() # long time + sage: len(UP) # long time + 450 + sage: UP[al[2],al[1],-al[1]] # long time + X0_7*X4_1 - X0_1*X4_7 - 2*X0_7*X5_1 + 2*X0_1*X5_7 + X2_7*X7_1 + - X2_1*X7_7 - X3_7*X8_1 + X3_1*X8_7 + X0_4 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing I = self.basis().keys() @@ -1480,7 +1490,11 @@ def sc(i, j): return s_coeffs[I[i],I[j]] d = {} keys = [] - R = PolynomialRing(self.base_ring(), ','.join('X{}{}'.format(i,j) + if n >= 10: + vs = 'X{}_{}' + else: + vs = 'X{}{}' + R = PolynomialRing(self.base_ring(), ','.join(vs.format(i,j) for i in range(n) for j in range(n))) X = [[R.gen(i+n*j) for i in range(n)] for j in range(n)] From 19db18f16dad8dd4db8440cc3a6841e0ba17ebb5 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 25 Aug 2020 07:21:30 +0100 Subject: [PATCH 355/379] correct '"""...' underlining --- src/doc/en/faq/faq-contribute.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index e484443e4d9..6fb8667c134 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -166,7 +166,7 @@ Also consult the Sage Developer's Guide, especially the chapter I submitted a bug fix to the trac server several weeks ago. Why are you ignoring my branch? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" We are not trying to ignore your branch. Most people who work on Sage do so in their free time. With hundreds of open tickets of varying degrees of @@ -198,7 +198,7 @@ ticket with an explanation why we cannot include your changes. When and how might I remind the Sage community of a branch I care about? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" You are encouraged to take extra care in how you remind the Sage community of a branch/patch you want to get merged into the Sage source From d9ed254ebfdd95ffb3c757a0ef8e7a49e8d3448f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 Aug 2020 10:38:57 +0200 Subject: [PATCH 356/379] some fixes suggested by lgtm (unused variables and imports, etc) --- src/sage/combinat/fully_packed_loop.py | 4 +-- src/sage/combinat/sloane_functions.py | 13 ++++----- .../graphs/generators/classical_geometries.py | 6 ++-- src/sage/quadratic_forms/genera/genus.py | 5 ++-- src/sage/repl/inputhook.py | 3 +- src/sage/repl/rich_output/backend_emacs.py | 3 +- src/sage_setup/autogen/giacpy-mkkeywords.py | 28 ++++++++----------- .../autogen/interpreters/generator.py | 2 -- 8 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index b901da4e61a..a27207ebae2 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -528,7 +528,7 @@ def __classcall_private__(cls, generator): generator = SixVertexModel(generator.parent()._nrows, boundary_conditions='ice')(generator) M = generator.to_alternating_sign_matrix().to_matrix() - M = AlternatingSignMatrix(M) + AlternatingSignMatrix(M) SVM = generator else: # Not ASM nor SVM try: @@ -537,7 +537,7 @@ def __classcall_private__(cls, generator): generator = matrix(generator) generator = SixVertexModel(generator.nrows(), boundary_conditions='ice')(generator) # Check that this is an ice square model - M = generator.to_alternating_sign_matrix() + generator.to_alternating_sign_matrix() SVM = generator if not SVM: diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 91edfc0f723..32c7cac054e 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -123,8 +123,7 @@ ######################################################################## # just used for handy .load, .save, etc. -from __future__ import print_function, absolute_import - +import sys import inspect from sage.structure.sage_object import SageObject from sage.arith.srange import srange @@ -135,6 +134,7 @@ Integer = ZZ + class SloaneSequence(SageObject): r""" Base class for a Sloane integer sequence. @@ -9692,9 +9692,9 @@ def trait_names(self): try: return self.__trait_names except AttributeError: - import sage.combinat.sloane_functions - xs = inspect.getmembers(sage.combinat.sloane_functions, inspect.isclass) - self.__trait_names = [ n for (n, c) in xs if n.startswith('A') and issubclass(c, SloaneSequence) ] + xs = inspect.getmembers(sys.modules[__name__], inspect.isclass) + self.__trait_names = [n for n, c in xs + if n.startswith('A') and issubclass(c, SloaneSequence)] return self.__trait_names def __getattribute__(self, name): @@ -9723,8 +9723,7 @@ def __getattribute__(self, name): return SageObject.__getattribute__(self, name) except AttributeError: try: - import sage.combinat.sloane_functions - f = getattr(sage.combinat.sloane_functions, name) + f = getattr(sys.modules[__name__], name) seq = f() setattr(self, name, seq) return seq diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 83e5109f362..6eb269c4f6e 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1394,7 +1394,6 @@ def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperov hyperoval = [x for x in Pi if (x[0] + x[1] * x[2] == 0) or (x[0] == 1 and x[1] == x[2] == 0)] - O = set(hyperoval) else: for v in hyperoval: v.set_immutable() @@ -1405,7 +1404,7 @@ def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperov raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): if set(L).issubset(Pi): - if not len(O.intersection(L)) in [0,2]: + if len(O.intersection(L)) not in [0, 2]: raise RuntimeError("incorrect hyperoval") M = matrix(hyperoval) F_0 = F.zero() @@ -1413,11 +1412,12 @@ def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperov for x in C: x.set_immutable() - G = Graph([C, lambda x,y: not F.zero() in x+y]) + G = Graph([C, lambda x,y: F.zero() not in x+y]) G.name('Nowhere0WordsTwoWeightCodeGraph('+str(q)+')') G.relabel() return G + def OrthogonalDualPolarGraph(e, d, q): r""" Return dual polar graph on $GO^e(n,q)$ of diameter `d`. diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index 62b6c13475c..52435da65ee 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -208,11 +208,10 @@ def _local_genera(p, rank, det_val, max_scale, even): # this is more work for the programmer if p == 2: for g in scales_rks: - n = len(g) poss_blocks = [] for b in g: b += [0, 0] - poss_blocks.append(_blocks(b, even_only=(even and b[0]==0))) + poss_blocks.append(_blocks(b, even_only=(even and b[0] == 0))) for g1 in cantor_product(*poss_blocks): g1 = list(g1) if is_2_adic_genus(g1): @@ -220,7 +219,7 @@ def _local_genera(p, rank, det_val, max_scale, even): # some of our symbols have the same canonical symbol # thus they are equivalent - we want only one in # each equivalence class - if not g1 in symbols: + if g1 not in symbols: symbols.append(g1) return symbols diff --git a/src/sage/repl/inputhook.py b/src/sage/repl/inputhook.py index 3709a29c189..5ea0d863cf8 100644 --- a/src/sage/repl/inputhook.py +++ b/src/sage/repl/inputhook.py @@ -12,10 +12,9 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ########################################################################### -import os import select import errno diff --git a/src/sage/repl/rich_output/backend_emacs.py b/src/sage/repl/rich_output/backend_emacs.py index 37477684fe4..e78f1127708 100644 --- a/src/sage/repl/rich_output/backend_emacs.py +++ b/src/sage/repl/rich_output/backend_emacs.py @@ -13,10 +13,9 @@ # 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/ #***************************************************************************** -import sys from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline from sage.repl.rich_output.output_catalog import * diff --git a/src/sage_setup/autogen/giacpy-mkkeywords.py b/src/sage_setup/autogen/giacpy-mkkeywords.py index fa5618ed716..3a918b20aeb 100644 --- a/src/sage_setup/autogen/giacpy-mkkeywords.py +++ b/src/sage_setup/autogen/giacpy-mkkeywords.py @@ -21,9 +21,7 @@ - Frederic Han (2020-07) """ - -import subprocess - +from subprocess import PIPE, Popen blacklist=['eval', 'cas_setup', 'i', 'list', 'input', 'in', 'sto', 'string', 'and', 'break', 'continue', 'else', 'for', 'from', 'if', 'not', 'or', 'pow', 'print', 'return', 'set[]', 'try', 'while', 'open', 'output', 'do', 'of', 'Request','i[]', '[]', 'ffunction', 'sleep', '[..]'] @@ -81,9 +79,7 @@ Mi.write(s) for i in mostkeywords+moremethods: - import subprocess - from subprocess import Popen - p = Popen(["cas_help", i], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + p = Popen(["cas_help", i], stdout=PIPE, stderr=PIPE, universal_newlines=True) doc = p.communicate()[0] doc = doc.replace("\n", "\n ") # Indent doc @@ -95,14 +91,12 @@ Mi.close() # building keywords.pxi -Fi=open("keywords.pxi","w") -Fi.write("# file auto generated by mkkeywords.py\n") -Fi.write("blacklist = "+str(blacklist)+"\n\n") -Fi.write("toremove = "+str(toremove)+"\n\n") -Fi.write("moremethods = "+str(moremethods)+"\n\n") -Fi.write("mostkeywords = "+str(mostkeywords)+"\n\n") -Fi.close() - -Fi=open("newkeyword.pxi","w") -Fi.write(str(list(set(mostkeywords).difference(mostkeywordorig)))) -Fi.close() +with open("keywords.pxi", "w") as Fi: + Fi.write("# file auto generated by mkkeywords.py\n") + Fi.write("blacklist = " + str(blacklist) + "\n\n") + Fi.write("toremove = " + str(toremove) + "\n\n") + Fi.write("moremethods = " + str(moremethods) + "\n\n") + Fi.write("mostkeywords = " + str(mostkeywords) + "\n\n") + +with open("newkeyword.pxi", "w") as Fi: + Fi.write(str(list(set(mostkeywords).difference(mostkeywordorig)))) diff --git a/src/sage_setup/autogen/interpreters/generator.py b/src/sage_setup/autogen/interpreters/generator.py index 63eca61b70c..f090f201afd 100644 --- a/src/sage_setup/autogen/interpreters/generator.py +++ b/src/sage_setup/autogen/interpreters/generator.py @@ -93,7 +93,6 @@ def gen_code(self, instr_desc, write): d = instr_desc w = write - s = self._spec if d.uses_error_handler: self.uses_error_handler = True @@ -173,7 +172,6 @@ def gen_code(self, instr_desc, write): stack_offsets = defaultdict(int) for i in range(len(d.inputs)): (ch, addr, input_len) = d.inputs[i] - chst = ch.storage_type if ch.is_python_refcounted_stack() and not d.handles_own_decref: if input_len is None: w(" Py_DECREF(i%d);\n" % i) From 10a1ca1fc537db4e9cfeb79f9afda00000010823 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 18:14:15 +0200 Subject: [PATCH 357/379] integer division in orthogonal dual polar --- src/sage/graphs/generators/classical_geometries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 83e5109f362..fc4cac3d48d 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1541,7 +1541,7 @@ def hashable(v): allIsoSubspaces = libgap.Orbit(permutation, isoSPointsInt, libgap.OnSets) # number of projective points in a (d-1)-subspace - intersection_size = (q**(d-1) - 1) / (q-1) + intersection_size = (q**(d-1) - 1) // (q-1) edges = [] n = len(allIsoSubspaces) From 254c6e932fa9f5b8f96dde02dda63936877157ea Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Tue, 25 Aug 2020 21:02:25 +0200 Subject: [PATCH 358/379] 29243: fix typo --- src/sage/matrix/matrix_double_dense.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 1333d64997e..81bdf23a589 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -1420,7 +1420,7 @@ cdef class Matrix_double_dense(Matrix_dense): sage: A = graphs.PetersenGraph().adjacency_matrix().change_ring(RDF) sage: ev = A.eigenvalues('symmetric', 1e-13) - doctest:...: DeprecationWarning: "extend" and "tol" should be used + doctest:...: DeprecationWarning: "algorithm" and "tol" should be used as keyword argument only See https://trac.sagemath.org/29243 for details. sage: ev # tol 1e-13 @@ -1440,7 +1440,7 @@ cdef class Matrix_double_dense(Matrix_dense): # for backward compatibilty, allow algorithm to be passed as first # positional argument and tol as second positional argument from sage.misc.superseded import deprecation - deprecation(29243, '"extend" and "tol" should be used as ' + deprecation(29243, '"algorithm" and "tol" should be used as ' 'keyword argument only') if algorithm != 'default': if isinstance(algorithm, str): From 282fda95b6baf610dae1f3ee9c694fe74b824eb6 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Tue, 25 Aug 2020 16:47:58 -0700 Subject: [PATCH 359/379] Fix preparser interfering with multi-line strings --- src/sage/repl/interpreter.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index f9d54427d45..b70345f9408 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -435,11 +435,21 @@ def SagePreparseTransformer(lines): SyntaxError: Mismatched ']' sage: shell.quit() + + Make sure the quote state is carried over across subsequent lines in order + to avoid interfering with multi-line strings, see :trac:`30417`. :: + + sage: SagePreparseTransformer(["'''", 'abc-1-2', "'''"]) + ["'''", 'abc-1-2', "'''"] + sage: # instead of ["'''", 'abc-Integer(1)-Integer(2)', "'''"] + """ lines_out = [] + reset = True for line in lines: if _do_preparse and not line.startswith('%'): - lines_out += [preparse(line)] + lines_out += [preparse(line, reset=reset)] + reset = False else: lines_out += [line] return lines_out From d4b2af2e29d691ec4af6828c9b2fd6a5516ed063 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 Aug 2020 08:26:40 +0200 Subject: [PATCH 360/379] reviewer commit --- build/pkgs/freetype/distros/nix.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/freetype/distros/nix.txt diff --git a/build/pkgs/freetype/distros/nix.txt b/build/pkgs/freetype/distros/nix.txt new file mode 100644 index 00000000000..098479093ff --- /dev/null +++ b/build/pkgs/freetype/distros/nix.txt @@ -0,0 +1 @@ +freetype From 250e3d77c71dac8fd23a1184f1a52a01820d4c0e Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 26 Aug 2020 08:45:52 +0200 Subject: [PATCH 361/379] change doctest to easier check --- src/sage/combinat/designs/gen_quadrangles_with_spread.pyx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index 645e43b4a33..753d935e862 100644 --- a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -254,8 +254,11 @@ def generalised_quadrangle_hermitian_with_ovoid(const int q): Incidence structure with 1105 points and 325 blocks sage: len(t[1]) 65 - sage: t[0].is_generalized_quadrangle() # long time - True + sage: G = t[0].intersection_graph([1]) # line graph + sage: G.is_strongly_regular(True) + (325, 68, 3, 17) + sage: set(t[0].block_sizes()) + {17} REFERENCES: From d29e166edb692179b386468c22d3aab07ffa8254 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 16 Sep 2019 09:08:05 +0200 Subject: [PATCH 362/379] fast path for the generator in generic polynomial _repr_() There are just too many things that do str(Foo.gen()) or similar all the time. --- src/sage/rings/polynomial/polynomial_element.pyx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 12e42d1dd77..808bdb0df9f 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -106,6 +106,7 @@ from sage.rings.integer cimport Integer, smallInteger from sage.libs.gmp.mpz cimport * from sage.rings.fraction_field import is_FractionField from sage.rings.padics.generic_nodes import is_pAdicRing, is_pAdicField +from sage.rings.padics.padic_generic import pAdicGeneric from sage.structure.category_object cimport normalize_names @@ -2524,10 +2525,14 @@ cdef class Polynomial(CommutativeAlgebraElement): x + 0 """ + if name is None: + name = self._parent._names[0] + # p-adic polynomials like (1 + O(p^20))*x have _is_gen set to True but + # want their coefficient printed with its O() term + if self._is_gen and not isinstance(self._parent._base, pAdicGeneric): + return name s = " " m = self.degree() + 1 - if name is None: - name = self._parent.variable_name() atomic_repr = self._parent.base_ring()._repr_option('element_is_atomic') coeffs = self.list(copy=False) for n in reversed(xrange(m)): From b792cbd249c916a27eabf532ea85778f9e0b97a0 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 22 Jul 2020 09:27:21 +0200 Subject: [PATCH 363/379] #30393 fix external links in docstrings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (Thanks to Sébastien Labbé for his explainations!) --- src/sage/matrix/matrix_complex_ball_dense.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx index dc0f8c6c346..b532233caad 100644 --- a/src/sage/matrix/matrix_complex_ball_dense.pyx +++ b/src/sage/matrix/matrix_complex_ball_dense.pyx @@ -677,7 +677,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): There is currently no guarantee that the algorithm converges as the working precision is increased. - See the `Arb documentation `_ + See the `Arb documentation `__ for more information. EXAMPLES:: @@ -746,7 +746,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): No guarantees are made about the accuracy of the output. - See the `Arb documentation `_ + See the `Arb documentation `__ for more information. EXAMPLES:: @@ -802,7 +802,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense): Additionally, there is currently no guarantee that the algorithm converges as the working precision is increased. - See the `Arb documentation `_ + See the `Arb documentation `__ for more information. EXAMPLES:: From a0463d55773dea371da684ff48732d6cbf1a2925 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 26 Aug 2020 16:04:44 +0200 Subject: [PATCH 364/379] fix docsrtings; renamed module --- src/doc/en/reference/combinat/module_list.rst | 2 +- src/sage/combinat/designs/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 39ca27c4a64..c8eca43954c 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -104,7 +104,7 @@ Comprehensive Module list sage/combinat/designs/difference_matrices sage/combinat/designs/evenly_distributed_sets sage/combinat/designs/ext_rep - sage/combinat/designs/gen_quadrangles + sage/combinat/designs/gen_quadrangles_with_spread sage/combinat/designs/incidence_structures sage/combinat/designs/latin_squares sage/combinat/designs/orthogonal_arrays diff --git a/src/sage/combinat/designs/__init__.py b/src/sage/combinat/designs/__init__.py index f8de34936f6..4808cf1a552 100644 --- a/src/sage/combinat/designs/__init__.py +++ b/src/sage/combinat/designs/__init__.py @@ -27,7 +27,7 @@ - :ref:`sage.combinat.designs.steiner_quadruple_systems` - :ref:`sage.combinat.designs.twographs` - :ref:`sage.combinat.designs.database` -- :ref:`sage.combinat.designs.gen_quadrangles` +- :ref:`sage.combinat.designs.gen_quadrangles_with_spread` **Technical things** From 5d16394c5bd7ba038f2a525a4759b7c197120100 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 26 Aug 2020 16:13:02 +0200 Subject: [PATCH 365/379] renamed in design_catalog --- src/sage/combinat/designs/design_catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 3a3c11597a9..a9572e119c0 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -59,7 +59,7 @@ :meth:`~sage.combinat.designs.steiner_quadruple_systems.steiner_quadruple_system` :meth:`~sage.combinat.designs.block_design.projective_plane` :meth:`~sage.combinat.designs.biplane` - :meth:`~sage.combinat.designs.gen_quadrangles` + :meth:`~sage.combinat.designs.gen_quadrangles_with_spread` And the :meth:`designs.best_known_covering_design_from_LJCR ` function From a3c790f6fb38fca37c37dae86e568a5acdedb1c0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 27 Aug 2020 10:06:55 +1000 Subject: [PATCH 366/379] Fix pdf docbuild in NumberFieldTower. --- src/sage/rings/number_field/number_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 1f98d497f2a..34dae8ef725 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -693,7 +693,7 @@ def create_object(self, version, key, check): NumberField_version2 = NumberFieldFactory("sage.rings.number_field.number_field.NumberField_version2") def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_names=None, assume_disc_small=False, maximize_at_primes=None, structures=None): - """ + r""" Create the tower of number fields defined by the polynomials in the list ``polynomials``. From cb701710ac20b6a1b74505b44d06ca5c15412fd2 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Thu, 27 Aug 2020 11:08:13 +0200 Subject: [PATCH 367/379] #28538 : typo --- src/sage/symbolic/assumptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index c0300286574..61350bb50ad 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -648,7 +648,7 @@ def assume(*args): [0 < x] sage: forget() - Chack that :trac:`28538` is fixed:: + Check that :trac:`28538` is fixed:: sage: x, y = SR.var('x, y') sage: assume(x > 0) From 3f1aad988e422578ded63d59b120d405fa7b4db4 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Thu, 27 Aug 2020 09:29:14 -0700 Subject: [PATCH 368/379] Recommend jupyterlab_widgets, mention jupyterlab as minimal install --- src/bin/sage-notebook | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook index 7cfe9a43f69..6d009bf81cc 100755 --- a/src/bin/sage-notebook +++ b/src/bin/sage-notebook @@ -54,6 +54,9 @@ class NotebookJupyterlab(object): except ImportError: print("Jupyterlab is not installed (at least not in this Sage installation).") print("You can install it by running") + print(" sage -i jupyterlab_widgets") + print("which includes support for interacts and the ability to download and") + print("install other Jupyterlab extensions. For a minimal installation, run") print(" sage -i jupyterlab") raise SystemExit(1) self.print_banner() From fc565ece0c80cbc3bebac5dcf4bbecc82232ab60 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 Aug 2020 09:35:29 -0700 Subject: [PATCH 369/379] src/sage/numerical/backends/matrix_sdp_backend.pyx: Docstring fix --- src/sage/numerical/backends/matrix_sdp_backend.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/numerical/backends/matrix_sdp_backend.pyx b/src/sage/numerical/backends/matrix_sdp_backend.pyx index b995ae41400..11dbaf5bb6e 100644 --- a/src/sage/numerical/backends/matrix_sdp_backend.pyx +++ b/src/sage/numerical/backends/matrix_sdp_backend.pyx @@ -173,7 +173,7 @@ cdef class MatrixSDPBackend(GenericSDPBackend): INPUT: - - ``sense`` (integer) : + - ``sense`` (integer) * +1 => Maximization * -1 => Minimization From bcbccd628ca923248a7e338b19dca9c1da9b3fad Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Thu, 27 Aug 2020 09:48:26 -0700 Subject: [PATCH 370/379] Mention jupyterlab in basic and advanced help messages --- src/bin/sage | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/sage b/src/bin/sage index f31d7e8ce0f..89b9022b1dd 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -33,8 +33,8 @@ usage() { echo " --maxima [...] -- run Sage's Maxima with given arguments" echo " --mwrank [...] -- run Sage's mwrank with given arguments" echo " --notebook=[...] -- start the Sage notebook (valid options are" - echo " 'default', 'jupyter', and 'export')" - echo " Current default is 'export' from sagenb to jupyter" + echo " 'default', 'jupyter', 'jupyterlab', and 'export')" + echo " Current default is 'jupyter'" echo " -n, --notebook -- shortcut for --notebook=default" echo " --python [...], --python3 [...] -- run the Python 3 interpreter" echo " -R [...] -- run Sage's R with given arguments" @@ -348,9 +348,9 @@ usage_advanced() { echo "Running the notebook:" echo echo " -n [...], --notebook=[...]" - echo " -- start the notebook; valid options" - echo " include \"default\", \"jupyter\", \"export\"." - echo " Current default is \"jupyter\"." + echo " -- start the notebook; valid options include" + echo " 'default', 'jupyter', 'jupyterlab', and 'export'." + echo " Current default is 'jupyter'." echo " Run \"sage --notebook --help\" for more details." echo #### 1.......................26..................................................78 From 0a06353ae7c91a8e66b6d1ead2561567c2b359b8 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Thu, 27 Aug 2020 09:54:46 -0700 Subject: [PATCH 371/379] Don't use grep -P in nodejs spkg-install --- build/pkgs/nodejs/spkg-install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/nodejs/spkg-install b/build/pkgs/nodejs/spkg-install index e2671b5ac68..731d8f3089d 100755 --- a/build/pkgs/nodejs/spkg-install +++ b/build/pkgs/nodejs/spkg-install @@ -4,7 +4,7 @@ if [ -z "$SAGE_LOCAL" ]; then exit 1 fi -nodejs_ver=`grep -oP "\d+\.\d+\.\d+" package-version.txt` +nodejs_ver=`grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" package-version.txt` if [ $? -ne 0 ]; then echo "Error determining which nodejs version to install ... exiting" @@ -34,7 +34,7 @@ if [ $? -ne 0 ]; then exit 1 fi -active_ver=`node --version | grep -oP "\d+\.\d+\.\d+"` +active_ver=`node --version | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+"` if [ $? -ne 0 ]; then echo "Error determining which nodejs version is active ... exiting" From 754dea963c990e3073bdd9ef77b83b87fc46ceb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 28 Aug 2020 09:54:34 +1200 Subject: [PATCH 372/379] Properly format tests in gbcore --- src/sage/rings/polynomial/pbori/gbcore.py | 64 ++++++++++++----------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index c690d4dcf25..e751c159c72 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -339,30 +339,32 @@ def ll_constants_pre(I): def variety_size_from_gb(I): """ - sage: from sage.rings.polynomial.pbori.frontend import * - sage: from sage.rings.polynomial.pbori.gbcore import variety_size_from_gb - sage: r=Ring(100) - sage: x = r.variable - sage: variety_size_from_gb([]) - 1 - sage: variety_size_from_gb([Polynomial(0, r)]) - 1 - sage: variety_size_from_gb([Polynomial(1, r)]) - 0.0 - sage: variety_size_from_gb([x(1)]) - 1.0 - sage: variety_size_from_gb([x(1), x(2)]) - 1.0 - sage: variety_size_from_gb([x(1), x(2)*x(3)]) - 3.0 - sage: variety_size_from_gb([x(1), x(1)*x(4), x(2)*x(3)]) - 6.0 - sage: variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) - 5.0 - sage: mons = [Monomial([r.variable(i) for i in range(100) if i!=j])\ - for j in range(100)] - sage: variety_size_from_gb(mons) - 1.2676506002282294e+30 + TESTS:: + + sage: from sage.rings.polynomial.pbori.frontend import * + sage: from sage.rings.polynomial.pbori.gbcore import variety_size_from_gb + sage: r=Ring(100) + sage: x = r.variable + sage: variety_size_from_gb([]) + 1 + sage: variety_size_from_gb([Polynomial(0, r)]) + 1 + sage: variety_size_from_gb([Polynomial(1, r)]) + 0.0 + sage: variety_size_from_gb([x(1)]) + 1.0 + sage: variety_size_from_gb([x(1), x(2)]) + 1.0 + sage: variety_size_from_gb([x(1), x(2)*x(3)]) + 3.0 + sage: variety_size_from_gb([x(1), x(1)*x(4), x(2)*x(3)]) + 6.0 + sage: variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) + 5.0 + sage: mons = [Monomial([r.variable(i) for i in range(100) if i!=j])\ + for j in range(100)] + sage: variety_size_from_gb(mons) + 1.2676506002282294e+30 """ I = [Polynomial(p) for p in I] I = [p for p in I if not p.is_zero()] @@ -389,12 +391,14 @@ def variety_size_from_gb(I): def other_ordering_pre(I, option_set, kwds): """ - sage: from sage.rings.polynomial.pbori.blocks import declare_ring - sage: r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) - sage: id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] - sage: from sage.rings.polynomial.pbori.gbcore import groebner_basis - sage: groebner_basis(id) - [1] + TESTS:: + + sage: from sage.rings.polynomial.pbori.blocks import declare_ring + sage: r = declare_ring(['x0', 'x1', 'x2', 'x3', 'x4'], globals()) + sage: id = [x1*x3 + x1 + x2*x3 + x3 + x4, x0*x3 + x0 + x1*x2 + x2 + 1, x1*x3 + x1*x4 + x3*x4 + x4 + 1, x0*x2 + x0*x4 + x1 + x3 + x4] + sage: from sage.rings.polynomial.pbori.gbcore import groebner_basis + sage: groebner_basis(id) + [1] """ if not I: From 91fe5e1c34a3d0b0e4a2960119aa313ad51e687e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 28 Aug 2020 10:33:52 +1200 Subject: [PATCH 373/379] Fix doctest and docstring formatting --- src/sage/rings/polynomial/pbori/parallel.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 944a0212854..1b89065ff48 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -31,15 +31,14 @@ def to_fast_pickable(l): It is converted to a tuple consisting of - codes referring to the polynomials - list of conversions of nodes. - The nodes are sorted, that - n occurs before n.else_branch(), n.then_branch() - Nodes are only listed, if they are not constant. + The nodes are sorted, so that n occurs before n.else_branch(), n.then_branch() + nodes are only listed, if they are not constant. A node is converted in this way: - 0 -> 0 - 1 -> 1 - if_then_else(v,t,e) -> (v, index of then branch +2, index of else branch +2) - The shift of +2 is for the constant values implicitly contained in the list. + 0 -> 0 + 1 -> 1 + if_then_else(v,t,e) -> (v, index of then branch +2, index of else branch +2) + the shift of +2 is for the constant values implicitly contained in the list. Each code c refers to the c-2-th position in the conversion list, if c >=2, else to the corresponding Boolean constant if c in {0, 1} @@ -272,7 +271,7 @@ def groebner_basis_first_finished(I, *l): OUTPUT: - - tries to compute groebner_basis(I, **kwd) for kwd in l + - tries to compute ``groebner_basis(I, **kwd)`` for kwd in l - returns the result of the first terminated computation EXAMPLES:: @@ -280,8 +279,9 @@ def groebner_basis_first_finished(I, *l): sage: from sage.rings.polynomial.pbori.PyPolyBoRi import Ring sage: r=Ring(1000) sage: ideal = [r.variable(1)*r.variable(2)+r.variable(2)+r.variable(1)] - sage: #groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) - [x(1), x(2)] + sage: from sage.rings.polynomial.pbori.parallel import groebner_basis_first_finished + sage: groebner_basis_first_finished(ideal, dict(heuristic=True), dict(heuristic=False)) + [x1, x2] """ if not I: return [] From e169060957d2aca9ba657599ca7f9fa4a3df23a1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 Aug 2020 21:50:13 -0700 Subject: [PATCH 374/379] build/pkgs/{jupyterlab_widgets,nodeenv}: Add dependencies --- build/pkgs/jupyterlab_widgets/dependencies | 2 +- build/pkgs/nodeenv/dependencies | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/nodeenv/dependencies diff --git a/build/pkgs/jupyterlab_widgets/dependencies b/build/pkgs/jupyterlab_widgets/dependencies index 2a670ff87ef..07346c2ff1a 100644 --- a/build/pkgs/jupyterlab_widgets/dependencies +++ b/build/pkgs/jupyterlab_widgets/dependencies @@ -1,4 +1,4 @@ -jupyterlab nodejs +jupyterlab nodejs tornado ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/nodeenv/dependencies b/build/pkgs/nodeenv/dependencies new file mode 100644 index 00000000000..dc5a209df7b --- /dev/null +++ b/build/pkgs/nodeenv/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) certifi + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. From 7dbfb245bca94607f90e79975238ef3de81fe21a Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Fri, 28 Aug 2020 21:54:37 +0200 Subject: [PATCH 375/379] #23002 : make bridges method for graphs an iterator --- src/sage/graphs/connectivity.pyx | 37 +++++++++++++++++++++----------- src/sage/graphs/generic_graph.py | 4 ++-- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 38e1a3a6bd4..651622e5efd 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -48,7 +48,7 @@ Here is what the module can do: :widths: 30, 70 :delim: | - :meth:`bridges` | Returns a list of the bridges (or cut edges) of given undirected graph. + :meth:`bridges` | Returns an iterator over the bridges (or cut edges) of given undirected graph. :meth:`cleave` | Return the connected subgraphs separated by the input vertex cut. :meth:`is_triconnected` | Check whether the graph is triconnected. :meth:`spqr_tree` | Return a SPQR-tree representing the triconnected components of the graph. @@ -1855,7 +1855,7 @@ def strong_articulation_points(G): def bridges(G, labels=True): r""" - Return a list of the bridges (or cut edges). + Return an iterator over the bridges (or cut edges). A bridge is an edge whose deletion disconnects the undirected graph. A disconnected graph has no bridge. @@ -1873,38 +1873,51 @@ def bridges(G, labels=True): sage: g.add_edge(1, 10) sage: is_connected(g) True - sage: bridges(g) + sage: list(list(bridges(g))) [(1, 10, None)] - sage: g.bridges() + sage: list(g.bridges()) [(1, 10, None)] + Every edge of a tree is a bridge:: + + sage: g = graphs.RandomTree(100) + sage: sum(1 for _ in g.bridges()) == 99 + True TESTS: + Disconnected graphs have no bridges:: + + sage: g = 2*graphs.PetersenGraph() + sage: next(g.bridges()) + Traceback (most recent call last): + ... + StopIteration + Graph with multiple edges and edge labels:: sage: g = 2 * graphs.CycleGraph(3) sage: g.allow_multiple_edges(True) sage: g.add_edges(g.edges(sort=False)) sage: g.add_edge(2, 3, "label") - sage: bridges(g, labels=True) + sage: list(bridges(g, labels=True)) [(2, 3, 'label')] Ticket :trac:`23817` is solved:: sage: G = Graph() sage: G.add_edge(0, 1) - sage: bridges(G) + sage: list(bridges(G)) [(0, 1, None)] sage: G.allow_loops(True) sage: G.add_edge(0, 0) sage: G.add_edge(1, 1) - sage: bridges(G) + sage: list(bridges(G)) [(0, 1, None)] If ``G`` is not a Sage Graph, an error is raised:: - sage: bridges('I am not a graph') + sage: next(bridges('I am not a graph')) Traceback (most recent call last): ... TypeError: the input must be an undirected Sage graph @@ -1915,7 +1928,7 @@ def bridges(G, labels=True): # Small graphs and disconnected graphs have no bridge if G.order() < 2 or not is_connected(G): - return [] + return B,C = G.blocks_and_cut_vertices() @@ -1923,7 +1936,6 @@ def bridges(G, labels=True): # multiple edges. cdef bint multiple_edges = G.allows_multiple_edges() cdef set ME = set(G.multiple_edges(labels=False)) if multiple_edges else set() - cdef list my_bridges = [] for b in B: if len(b) == 2 and not tuple(b) in ME: if labels: @@ -1931,11 +1943,10 @@ def bridges(G, labels=True): [label] = G.edge_label(b[0], b[1]) else: label = G.edge_label(b[0], b[1]) - my_bridges.append((b[0], b[1], label)) + yield (b[0], b[1], label) else: - my_bridges.append(tuple(b)) + yield tuple(b) - return my_bridges # ============================================================================== # Methods for finding 3-vertex-connected components and building SPQR-tree diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 3fbc28d75dd..e39d555e585 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -8977,8 +8977,8 @@ def nowhere_zero_flow(self, k=None, solver=None, verbose=0): return solution # If the (di)graph has bridges, the problem is not feasible - if ( (self.is_directed() and not self.is_strongly_connected() and self.to_undirected().bridges()) - or (not self.is_directed() and self.bridges()) ): + if ( (self.is_directed() and not self.is_strongly_connected() and next(self.to_undirected().bridges(), False)) + or (not self.is_directed() and next(self.bridges(), False)) ): raise EmptySetError("(di)graphs with bridges have no feasible solution") # From 5a4286984f499cd442d3e2018644d593b7924634 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Fri, 28 Aug 2020 22:47:38 +0200 Subject: [PATCH 376/379] #23002 : typo --- src/sage/graphs/connectivity.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 651622e5efd..c540a14068d 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -1873,7 +1873,7 @@ def bridges(G, labels=True): sage: g.add_edge(1, 10) sage: is_connected(g) True - sage: list(list(bridges(g))) + sage: list(bridges(g)) [(1, 10, None)] sage: list(g.bridges()) [(1, 10, None)] From 4e167e9965f92b73597adc90968ec0162ab08c13 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sat, 29 Aug 2020 14:04:59 -0700 Subject: [PATCH 377/379] trac 29651: docbuild a single file --- src/sage_setup/docbuild/__init__.py | 5 ++++- src/sage_setup/docbuild/sphinxbuild.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index aee7d609a44..7f868a1b8f1 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -60,7 +60,7 @@ import sage.all from sage.misc.cachefunc import cached_method from sage.misc.misc import sage_makedirs -from sage.env import SAGE_DOC_SRC, SAGE_DOC, SAGE_SRC +from sage.env import SAGE_DOC_SRC, SAGE_DOC, SAGE_SRC, DOT_SAGE from .build_options import (LANGUAGES, SPHINXOPTS, PAPER, OMIT, PAPEROPTS, ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS, @@ -1186,6 +1186,9 @@ def __init__(self, path): html_short_title = project htmlhelp_basename = name +extensions.remove('multidocs') # see #29651 +extensions.remove('inventory_builder') + latex_domain_indices = False latex_documents = [ ('index', name + '.tex', u'Documentation for {}', diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index fe7eba43b2a..b08ea9b7421 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -110,7 +110,12 @@ def _init_chatter(self): re.compile('WARNING: Any IDs not assiend for figure node'), re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), - ) + ) + # The warning "unknown config value 'multidoc_first_pass'..." + # should only appear when building the documentation for a + # single file (SingleFileBuilder from __init__.py), and it + # needs to be ignored in that case. See #29651. + self._ignored_warnings += (re.compile('WARNING: unknown config value \'multidoc_first_pass\''),) self._useless_chatter += self._ignored_warnings # replacements: pairs of regular expressions and their replacements, From b0310af578810d6b99e525c68bbc01fc523f672e Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sat, 29 Aug 2020 15:47:29 -0700 Subject: [PATCH 378/379] trac 29651: fix module names with underscores --- src/sage_setup/docbuild/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 7f868a1b8f1..1611f0b6460 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -1156,7 +1156,7 @@ def __init__(self, path): # By default, this is DOT_SAGE/docbuild/MODULE_NAME, but can # also be specified at the command line. module_name = os.path.splitext(os.path.basename(path))[0] - latex_name = module_name.replace('_', r'\_') + latex_name = module_name.replace('_', r'\\_') if self._options.output_dir: base_dir = os.path.join(self._options.output_dir, module_name) From c353bce99541d6e992ea134744bf130e32ffaf18 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Wed, 2 Sep 2020 01:06:11 +0200 Subject: [PATCH 379/379] Updated SageMath version to 9.2.beta11 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index c9a26821404..95ff999201e 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.2.beta10, Release Date: 2020-08-23 +SageMath version 9.2.beta11, Release Date: 2020-09-01 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index edf44dacfe3..7be943210d0 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=8cdc25191e2927a57663b2701c58882c392a025a -md5=aa651c4ebcddea7fa300575f7cb7aaf0 -cksum=3086845000 +sha1=bc02b89295bae975608586b7717f5bc6d6e6ef61 +md5=1f20546274d26f840ffd3001a077e341 +cksum=690405059 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 580ec43962b..0dfb70eb5f4 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -e81d1b0a2d71d0fad157bfd8698803318e53e5b0 +1b9d7cff30fd43e7133ef06b53f646453e35cfa1 diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index 9d8474538dd..be3bc93cc55 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.2.beta10 +9.2.beta11 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 4e2829257a1..ad4531325c1 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.2.beta10' -SAGE_RELEASE_DATE='2020-08-23' -SAGE_VERSION_BANNER='SageMath version 9.2.beta10, Release Date: 2020-08-23' +SAGE_VERSION='9.2.beta11' +SAGE_RELEASE_DATE='2020-09-01' +SAGE_VERSION_BANNER='SageMath version 9.2.beta11, Release Date: 2020-09-01' diff --git a/src/sage/version.py b/src/sage/version.py index 8597b03e73c..be1f0e3ff42 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 = '9.2.beta10' -date = '2020-08-23' -banner = 'SageMath version 9.2.beta10, Release Date: 2020-08-23' +version = '9.2.beta11' +date = '2020-09-01' +banner = 'SageMath version 9.2.beta11, Release Date: 2020-09-01'