From 9a9e4d803feaa70dd30989ed4647b1f13581a5cf Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Sun, 25 Jul 2021 15:13:51 -0400 Subject: [PATCH 001/122] Remove deprecated parameters and methods in projective_ds --- .../arithmetic_dynamics/projective_ds.py | 143 +----------------- 1 file changed, 5 insertions(+), 138 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 7b53546f1ee..df9ef518420 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -4787,7 +4787,8 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point'): multiplier spectra, which includes the multipliers of all periodic points of period ``n`` - - ``embedding`` -- deprecated in :trac:`23333` + - ``embedding`` -- (default: ``None``) must be ``None``, passing an embedding + is no longer supported, see :trac: `32205`. - ``type`` -- (default: ``'point'``) string; either ``'point'`` or ``'cycle'`` depending on whether you compute with one @@ -4892,6 +4893,9 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point'): [2, 4*t/(t^2 + 1), 0] """ n = ZZ(n) + + if not embedding is None: + raise ValueError('do not specify an embedding') if n < 1: raise ValueError("period must be a positive integer") dom = self.domain() @@ -4899,9 +4903,6 @@ def sigma_invariants(self, n, formal=False, embedding=None, type='point'): raise NotImplementedError("not implemented for subschemes") if dom.dimension_relative() > 1: raise NotImplementedError("only implemented for dimension 1") - if not embedding is None: - from sage.misc.superseded import deprecation - deprecation(23333, "embedding keyword no longer used") if self.degree() <= 1: raise TypeError("must have degree at least 2") if not type in ['point', 'cycle']: @@ -5896,72 +5897,6 @@ def all_periodic_points(self, **kwds): else: raise TypeError("base field must be an absolute number field") - def rational_periodic_points(self, **kwds): - r""" - Determine the set of rational periodic points - for this dynamical system. - - The map must be defined over `\QQ` and be an endomorphism of - projective space. If the map is a polynomial endomorphism of - `\mathbb{P}^1`, i.e. has a totally ramified fixed point, then - the base ring can be an absolute number field. - This is done by passing to the Weil restriction. - - The default parameter values are typically good choices for - `\mathbb{P}^1`. If you are having trouble getting a particular - map to finish, try first computing the possible periods, then - try various different ``lifting_prime`` values. - - ALGORITHM: - - Modulo each prime of good reduction `p` determine the set of - periodic points modulo `p`. For each cycle modulo `p` compute - the set of possible periods (`mrp^e`). Take the intersection - of the list of possible periods modulo several primes of good - reduction to get a possible list of minimal periods of rational - periodic points. Take each point modulo `p` associated to each - of these possible periods and try to lift it to a rational point - with a combination of `p`-adic approximation and the LLL basis - reduction algorithm. - - See [Hutz2015]_. - - INPUT: - - kwds: - - - ``prime_bound`` -- (default: ``[1,20]``) a pair (list or tuple) - of positive integers that represent the limits of primes to use - in the reduction step or an integer that represents the upper bound - - - ``lifting_prime`` -- (default: 23) a prime integer; argument that - specifies modulo which prime to try and perform the lifting - - - ``periods`` -- (optional) a list of positive integers that is - the list of possible periods - - - ``bad_primes`` -- (optional) a list or tuple of integer primes; - the primes of bad reduction - - - ``ncpus`` -- (default: all cpus) number of cpus to use in parallel - - OUTPUT: a list of rational points in projective space - - EXAMPLES:: - - sage: R. = QQ[] - sage: K. = NumberField(x^2-x+1) - sage: P. = ProjectiveSpace(K,1) - sage: f = DynamicalSystem_projective([u^2 + v^2,v^2]) - sage: sorted(f.rational_periodic_points()) - doctest:warning - ... - [(-w + 1 : 1), (w : 1), (1 : 0)] - """ - from sage.misc.superseded import deprecation - deprecation(28109, "use sage.dynamics.arithmetic_dynamics.projective_ds.all_periodic_points instead") - return self.all_periodic_points(**kwds) - def all_rational_preimages(self, points): r""" Given a set of rational points in the domain of this @@ -6045,74 +5980,6 @@ def all_rational_preimages(self, points): preperiodic.add(preimages[i]) return list(preperiodic) - def rational_preperiodic_points(self, **kwds): - r""" - Determine the set of rational preperiodic points for - this dynamical system. - - The map must be defined over `\QQ` and be an endomorphism of - projective space. If the map is a polynomial endomorphism of - `\mathbb{P}^1`, i.e. has a totally ramified fixed point, then - the base ring can be an absolute number field. - This is done by passing to the Weil restriction. - - The default parameter values are typically good choices for - `\mathbb{P}^1`. If you are having trouble getting a particular - map to finish, try first computing the possible periods, then - try various different values for ``lifting_prime``. - - ALGORITHM: - - - Determines the list of possible periods. - - - Determines the rational periodic points from the possible periods. - - - Determines the rational preperiodic points from the rational - periodic points by determining rational preimages. - - INPUT: - - kwds: - - - ``prime_bound`` -- (default: ``[1, 20]``) a pair (list or tuple) - of positive integers that represent the limits of primes to use - in the reduction step or an integer that represents the upper bound - - - ``lifting_prime`` -- (default: 23) a prime integer; specifies - modulo which prime to try and perform the lifting - - - ``periods`` -- (optional) a list of positive integers that is - the list of possible periods - - - ``bad_primes`` -- (optional) a list or tuple of integer primes; - the primes of bad reduction - - - ``ncpus`` -- (default: all cpus) number of cpus to use in parallel - - - ``period_degree_bounds`` -- (default: ``[4,4]``) a pair of positive integers - (max period, max degree) for which the dynatomic polynomial should be solved - for when in dimension 1 - - - ``algorithm`` -- (optional) specifies which algorithm to use; - current options are `dynatomic` and `lifting`; defaults to solving the - dynatomic for low periods and degrees and lifts for everything else - - OUTPUT: a list of rational points in projective space - - EXAMPLES:: - - sage: PS. = ProjectiveSpace(1,QQ) - sage: f = DynamicalSystem_projective([x^2 -y^2, 3*x*y]) - sage: sorted(f.rational_preperiodic_points()) - doctest:warning - ... - [(-2 : 1), (-1 : 1), (-1/2 : 1), (0 : 1), (1/2 : 1), (1 : 0), (1 : 1), - (2 : 1)] - """ - from sage.misc.superseded import deprecation - deprecation(28213, "use sage.dynamics.arithmetic_dynamics.projective_ds.all_preperiodic_points instead") - return self.all_preperiodic_points(**kwds) - def all_preperiodic_points(self, **kwds): r""" Determine the set of rational preperiodic points for From cca4acd6e49c999adb734a2a1b91dd51036a7acd Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 28 Aug 2021 23:03:36 +0800 Subject: [PATCH 002/122] EllipticCurveIsogeny.is_normalized: remove obsolete algorithm --- .../elliptic_curves/ell_curve_isogeny.py | 95 ++----------------- 1 file changed, 9 insertions(+), 86 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index ebbd2d2fc0c..774e26a8281 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -3139,7 +3139,7 @@ def switch_sign(self): """ self.set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3()))) - def is_normalized(self, via_formal=True, check_by_pullback=True): + def is_normalized(self, via_formal=None, check_by_pullback=None): r""" Return whether this isogeny is normalized. @@ -3152,14 +3152,10 @@ def is_normalized(self, via_formal=True, check_by_pullback=True): differentials on `E` and `E_2` corresponding to the given equation. - INPUT: - - - ``via_formal`` - (default: ``True``) If ``True`` it simply - checks if the leading term of the formal series is - 1. Otherwise it uses a deprecated algorithm involving the - second optional argument. + ALGORITHM: - - ``check_by_pullback`` - (default:``True``) Deprecated. + The method checks if the leading term of the formal series + associated to this isogeny equals 1. EXAMPLES:: @@ -3220,84 +3216,11 @@ def is_normalized(self, via_formal=True, check_by_pullback=True): sage: phi.is_normalized() True """ - # easy algorithm using the formal expansion. - if via_formal: - phi_formal = self.formal(prec=5) - return phi_formal[1] == 1 - - # this is the old algorithm. it should be deprecated. - check_prepost_isomorphism = False - - f_normalized = True - - if (check_by_pullback): - - (Xmap, Ymap) = self.rational_maps() - - E1 = self.__E1 - E2 = self.__E2 - - a1 = E1.a1() - a3 = E1.a3() - - a1pr = E2.a1() - a3pr = E2.a3() - - x, y = self.__mpoly_ring.gens() - - Xmap_pr = Xmap.derivative(x) - - domain_inv_diff = 1/(2*y + a1*x + a3) - codomain_inv_diff = Xmap_pr/(2*Ymap + a1pr*Xmap + a3pr) - - inv_diff_quo = domain_inv_diff/codomain_inv_diff - - if (1 == inv_diff_quo): - f_normalized = True - else: - # For some reason, in certain cases, when the isogeny - # is pre or post composed with a translation the - # resulting rational functions are too complicated for - # sage to simplify down to a constant in this case, we - # do some cheating by checking if the post-composition - # by isogeny has a non 1 scaling factor - if ( inv_diff_quo.numerator().is_constant() and (inv_diff_quo.denominator().is_constant) ): - f_normalized = False - else: - check_prepost_isomorphism = True - else: - check_prepost_isomorphism = True - - # If we skip checking by the pullback of the invariant - # differential OR if that was inconclusive We explicitly check - # if there is a post isomorphism and if it has a non 1 scaling - # factor or if it is a just a translation. NOTE: This only - # works because we are using algorithms for calculating the - # isogenies that calculate a separable normalized isogeny, if - # this changes, this check will no longer be correct. - # - if (check_prepost_isomorphism): - post_isom = self.__post_isomorphism - if (post_isom is not None): - if (1 == self.__base_field(post_isom.u)): - f_post_normalized = True - else: - f_post_normalized = False - else: - f_post_normalized = True - - pre_isom = self.__pre_isomorphism - if (pre_isom is not None): - if (1 == self.__base_field(pre_isom.u)): - f_pre_normalized = True - else: - f_pre_normalized = False - else: - f_pre_normalized = True - - f_normalized = f_pre_normalized and f_post_normalized - - return f_normalized + if via_formal is not None or check_by_pullback is not None: + from sage.misc.superseded import deprecation + deprecation(32430, 'The "via_formal" and "check_by_pullback" arguments are obsolete, have no effect, and will be removed at some point.') + phi_formal = self.formal(prec=5) + return phi_formal[1] == 1 def dual(self): r""" From 7a41934e686072fe61fbc4eca16f07a6a8ed2f8f Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 31 Aug 2021 11:20:33 +0800 Subject: [PATCH 003/122] call .shift() in __{l,r}shift__ to fix #32440 --- .../rings/laurent_series_ring_element.pyx | 6 +++ src/sage/rings/power_series_ring_element.pyx | 39 +++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index f0010cb2e27..b9f09471f39 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -1753,6 +1753,12 @@ cdef class LaurentSeries(AlgebraElement): Traceback (most recent call last): ... TypeError: self is not a power series + + Test for :trac:`32440`:: + + sage: L. = LaurentSeriesRing(QQ, implementation='pari') + sage: (x + O(x^3)).power_series() + x + O(x^3) """ if self.__n < 0: if self.__u.is_zero() and self.__u.prec() >= - self.__n: diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index a021390d012..4a2595b13b0 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1190,13 +1190,46 @@ cdef class PowerSeries(AlgebraElement): - Robert Bradshaw (2007-04-18) """ - return self._parent(self.polynomial().shift(n), self._prec + n) + if not n: + return self + prec = max(0, self.prec() + Integer(n)) + return self._parent(self.polynomial().shift(n), prec) def __lshift__(self, n): - return self.parent()(self.polynomial() << n, self.prec()) + """ + Left-shift this power series by `n`, i.e., multiply by `t^n`. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(QQ, implementation='pari') + sage: f = exp(x) + O(x^7); f + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) + sage: f << 2 + x^2 + x^3 + 1/2*x^4 + 1/6*x^5 + 1/24*x^6 + 1/120*x^7 + 1/720*x^8 + O(x^9) + sage: (f << 99) >> 99 + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + O(x^7) + """ + return self.shift(n) def __rshift__(self, n): - return self.parent()(self.polynomial() >> n, self.prec()) + """ + Right-shift this power series by `n`, i.e., divide by `t^n`. + Terms below `t^n` are discarded. + + EXAMPLES:: + + sage: R. = PowerSeriesRing(QQ, implementation='pari') + sage: f = exp(x) + O(x^7) + sage: f >> 3 + 1/6 + 1/24*x + 1/120*x^2 + 1/720*x^3 + O(x^4) + sage: f >> 7 + O(x^0) + sage: f >> 99 + O(x^0) + sage: (f >> 99) << 99 + O(x^99) + """ + return self.shift(-n) def is_monomial(self): """ From a824ddae9186552f8e02aa626652919834825990 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 31 Aug 2021 19:44:17 +0800 Subject: [PATCH 004/122] doc tweaks --- src/sage/rings/power_series_ring_element.pyx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 4a2595b13b0..9142daa13b3 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1160,17 +1160,18 @@ cdef class PowerSeries(AlgebraElement): def shift(self, n): r""" - Return this power series multiplied by the power `t^n`. If - `n` is negative, terms below `t^n` will be - discarded. Does not change this power series. + Return this power series multiplied by the power `t^n`. + + If `n` is negative, terms below `t^{-n}` are discarded. + + This power series is left unchanged. .. NOTE:: Despite the fact that higher order terms are printed to the right in a power series, right shifting decreases the - powers of `t`, while left shifting increases - them. This is to be consistent with polynomials, integers, - etc. + powers of `t`, while left shifting increases them. + This is to be consistent with polynomials, integers, etc. EXAMPLES:: @@ -1214,6 +1215,7 @@ cdef class PowerSeries(AlgebraElement): def __rshift__(self, n): """ Right-shift this power series by `n`, i.e., divide by `t^n`. + Terms below `t^n` are discarded. EXAMPLES:: From 834500a167e98e01292f0be9557f47ab667378de Mon Sep 17 00:00:00 2001 From: Charles Bouillaguet Date: Wed, 1 Sep 2021 09:21:11 +0200 Subject: [PATCH 005/122] Do not access M4RI internal Remove references to the rows[...] array and use mzd_row() instead. The rows[...] array may disappear in the future. --- src/sage/libs/m4ri.pxd | 4 ++-- src/sage/matrix/matrix_mod2_dense.pyx | 6 ++++-- src/sage/modules/vector_mod2_dense.pyx | 13 +++++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/sage/libs/m4ri.pxd b/src/sage/libs/m4ri.pxd index cae979eb618..a9c6c792c05 100644 --- a/src/sage/libs/m4ri.pxd +++ b/src/sage/libs/m4ri.pxd @@ -11,8 +11,6 @@ cdef extern from "m4ri/m4ri.h": rci_t nrows rci_t ncols wi_t width - int offset - m4ri_word **rows ctypedef struct mzp_t: rci_t *values @@ -88,6 +86,8 @@ cdef extern from "m4ri/m4ri.h": # Row/Column Based IO ##################### + cdef m4ri_word *mzd_row(mzd_t *, rci_t) + cdef void mzd_row_swap(mzd_t *, rci_t, rci_t) cdef void mzd_col_swap(mzd_t *, rci_t, rci_t) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 8f1374eb39f..00f5a2e0ab9 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1214,17 +1214,19 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse cdef m4ri_word mask = 0 # Original code, before adding the ``nonzero`` option. + cdef m4ri_word *row if not nonzero: if density == 1: assert(sizeof(m4ri_word) == 8) mask = __M4RI_LEFT_BITMASK(self._entries.ncols % m4ri_radix) for i from 0 <= i < self._nrows: + row = mzd_row(self._entries, i) for j from 0 <= j < self._entries.width: # for portability we get 32-bit twice rather than 64-bit once low = gmp_urandomb_ui(rstate.gmp_state, 32) high = gmp_urandomb_ui(rstate.gmp_state, 32) - self._entries.rows[i][j] = m4ri_swap_bits( ((high)<<32) | (low) ) - self._entries.rows[i][self._entries.width - 1] &= mask + row[j] = m4ri_swap_bits( ((high)<<32) | (low) ) + row[self._entries.width - 1] &= mask else: nc = self._ncols num_per_row = int(density * nc) diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index 3c08d14f2d7..f996d1e821e 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -344,8 +344,9 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): """ cdef int i cdef int res = 0 + cdef m4ri_word *row = mzd_row(self._entries, 0) for i from 0 <= i < self._entries.width: - res += Integer(self._entries.rows[0][i]).popcount() + res += Integer(row[i]).popcount() return res @@ -386,9 +387,10 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): n = IntegerMod_int.__new__(IntegerMod_int) IntegerMod_abstract.__init__(n, self.base_ring()) n.ivalue = 0 - + cdef m4ri_word *lrow = mzd_row(self._entries, 0) + cdef m4ri_word *rrow = mzd_row(r._entries, 0) for i from 0 <= i < self._entries.width: - tmp ^= self._entries.rows[0][i] & r._entries.rows[0][i] + tmp ^= lrow[i] & rrow[i] for i in range(64): n.ivalue ^= (tmp & 1) @@ -411,8 +413,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): r = right z = self._new_c() cdef Py_ssize_t i + cdef m4ri_word *lrow = mzd_row(self._entries, 0) + cdef m4ri_word *rrow = mzd_row(r._entries, 0) + cdef m4ri_word *zrow = mzd_row(z._entries, 0) for i from 0 <= i < self._entries.width: - z._entries.rows[0][i] = (self._entries.rows[0][i] & r._entries.rows[0][i]) + zrow[i] = (lrow[i] & rrow[i]) return z cpdef _lmul_(self, Element left): From 59a7af40f70b2ecbb28e89b82d51e694e512e709 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 1 Sep 2021 18:38:40 +0800 Subject: [PATCH 006/122] fix doctest formatting --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index ebbd2d2fc0c..2f023494dc3 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -3391,7 +3391,7 @@ def dual(self): sage: phi.dual() Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 + 84*x + 34 over Finite Field of size 103 to Elliptic Curve defined by y^2 + x*y = x^3 + x + 102 over Finite Field of size 103 - Check that :trac:`17293` is fixed: + Check that :trac:`17293` is fixed:: sage: k. = QuadraticField(2) sage: E = EllipticCurve(k, [-3*s*(4 + 5*s), 2*s*(2 + 14*s + 11*s^2)]) From 3a9e585cdecd3bfc9445c70741a342cf08fe604a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 4 Sep 2021 09:13:44 +0800 Subject: [PATCH 007/122] probably no need to warn after >10 years of deprecation --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 774e26a8281..34f9f424ab0 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -3139,7 +3139,7 @@ def switch_sign(self): """ self.set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3()))) - def is_normalized(self, via_formal=None, check_by_pullback=None): + def is_normalized(self): r""" Return whether this isogeny is normalized. @@ -3216,9 +3216,6 @@ def is_normalized(self, via_formal=None, check_by_pullback=None): sage: phi.is_normalized() True """ - if via_formal is not None or check_by_pullback is not None: - from sage.misc.superseded import deprecation - deprecation(32430, 'The "via_formal" and "check_by_pullback" arguments are obsolete, have no effect, and will be removed at some point.') phi_formal = self.formal(prec=5) return phi_formal[1] == 1 From e470cf8f99ad7d9f38a5bdebbf7313dfcc0d1e10 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Tue, 3 Aug 2021 19:41:36 +0200 Subject: [PATCH 008/122] libs: ecl: simplify conversion of unicode strings to and from ecl The new method is both simpler and faster than the old one. --- src/sage/libs/ecl.pxd | 10 ++++++++++ src/sage/libs/ecl.pyx | 22 ++++------------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/sage/libs/ecl.pxd b/src/sage/libs/ecl.pxd index 43a41c0c97b..9c6fa118c5d 100644 --- a/src/sage/libs/ecl.pxd +++ b/src/sage/libs/ecl.pxd @@ -36,6 +36,7 @@ cdef extern from "ecl/ecl.h": ctypedef cl_fixnum cl_narg ctypedef void *cl_object ctypedef unsigned long int cl_index + ctypedef int ecl_character ctypedef enum ecl_option: ECL_OPT_INCREMENTAL_GC = 0, @@ -108,6 +109,7 @@ cdef extern from "ecl/ecl.h": # ECL numeric type conversion + cl_object ecl_make_fixnum(cl_fixnum i) cl_object ecl_make_integer(cl_fixnum i) cl_object ecl_make_unsigned_integer(cl_index i) cl_fixnum ecl_fixint "fixint" (cl_object x) @@ -141,6 +143,10 @@ cdef extern from "ecl/ecl.h": cl_object ecl_cstring_to_base_string_or_nil(char *s) cl_object si_coerce_to_base_string(cl_object x) cl_object si_base_string_p(cl_object x) + cl_object cl_make_string(cl_narg narg, cl_object o, ...) + ecl_character ecl_char(cl_object s, cl_index i) + ecl_character ecl_char_set(cl_object s, cl_index i, ecl_character c) + # S-expr evaluation and function calls @@ -159,3 +165,7 @@ cdef extern from "ecl/ecl.h": # symbols cl_object ecl_make_symbol(const char *name, const char *package) + + # sequences + + cl_fixnum ecl_length(cl_object x) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 57d5fdd9108..91911c598d2 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -235,8 +235,6 @@ def init_ecl(): """ global list_of_objects global read_from_string_clobj - global make_unicode_string_clobj - global unicode_string_codepoints_clobj global conditions_to_handle_clobj global ecl_has_booted cdef char *argv[1] @@ -291,26 +289,13 @@ def init_ecl(): conditions_to_handle_clobj=ecl_list1(ecl_make_symbol(b"SERIOUS-CONDITION", b"COMMON-LISP")) insert_node_after(list_of_objects,conditions_to_handle_clobj) - cl_eval(string_to_object(b""" - (defun sage-make-unicode-string (codepoints) - (map 'string #'code-char codepoints)) - """)) - make_unicode_string_clobj = cl_eval(string_to_object(b"#'sage-make-unicode-string")) - - cl_eval(string_to_object(b""" - (defun sage-unicode-string-codepoints (s) - (map 'list #'char-code s)) - """)) - unicode_string_codepoints_clobj = cl_eval(string_to_object(b"#'sage-unicode-string-codepoints")) - ecl_has_booted = 1 cdef ecl_string_to_python(cl_object s): if bint_base_string_p(s): return char_to_str(ecl_base_string_pointer_safe(s)) else: - s = cl_funcall(2, unicode_string_codepoints_clobj, s) - return ''.join(chr(code) for code in ecl_to_python(s)) + return ''.join(chr(ecl_char(s, i)) for i in range(ecl_length(s))) cdef cl_object ecl_safe_eval(cl_object form) except NULL: """ @@ -455,8 +440,9 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: try: s = str_to_bytes(pyobj, 'ascii') except UnicodeEncodeError: - o = cl_funcall(2, make_unicode_string_clobj, - python_to_ecl([ord(c) for c in pyobj], read_strings)) + o = cl_make_string(1, ecl_make_fixnum(len(pyobj))) + for i in range(len(pyobj)): + ecl_char_set(o, i, ord(pyobj[i])) else: o = ecl_cstring_to_base_string_or_nil(s) From 9c2da2dc7b76c0d79c5a3b84343a865864b9a5a3 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 8 Aug 2021 20:19:04 +0200 Subject: [PATCH 009/122] libs: ecl: simplify conversion of lists and tuples By building the Lisp list in reverse, the code becomes shorter and more efficient. --- src/sage/libs/ecl.pyx | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 91911c598d2..d9c995662cf 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -468,27 +468,17 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: elif isinstance(pyobj,EclObject): return (pyobj).obj elif isinstance(pyobj, list): - if not pyobj: - return Cnil - else: - L = cl_cons(python_to_ecl(pyobj[0], read_strings),Cnil) - ptr = L - for a in pyobj[1:]: - cl_rplacd(ptr, cl_cons(python_to_ecl(a, read_strings), Cnil)) - ptr = cl_cdr(ptr) - return L + L = Cnil + for i in range(len(pyobj)-1,-1,-1): + L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) + return L elif isinstance(pyobj, tuple): if not pyobj: return Cnil - elif len(pyobj) == 1: - return python_to_ecl(pyobj[0], read_strings) else: - L = cl_cons(python_to_ecl(pyobj[0], read_strings), Cnil) - ptr = L - for a in pyobj[1:-1]: - cl_rplacd(ptr, cl_cons(python_to_ecl(a, read_strings), Cnil)) - ptr = cl_cdr(ptr) - cl_rplacd(ptr, python_to_ecl(pyobj[-1], read_strings)) + L = python_to_ecl(pyobj[-1], read_strings) + for i in range(len(pyobj)-2,-1,-1): + L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) return L else: raise TypeError("Unimplemented type for python_to_ecl") From cf49bfe8f0e940183b9f08c37c3dec7dd92445ff Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 8 Aug 2021 20:23:47 +0200 Subject: [PATCH 010/122] libs: ecl: remove deprecated Cnil/Ct names --- src/sage/libs/ecl.pxd | 4 ++-- src/sage/libs/ecl.pyx | 38 +++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/libs/ecl.pxd b/src/sage/libs/ecl.pxd index 9c6fa118c5d..19472171403 100644 --- a/src/sage/libs/ecl.pxd +++ b/src/sage/libs/ecl.pxd @@ -73,8 +73,8 @@ cdef extern from "ecl/ecl.h": # predefined symbols - cl_object Cnil - cl_object Ct + cl_object ECL_NIL + cl_object ECL_T cl_fixnum MOST_POSITIVE_FIXNUM cl_fixnum MOST_NEGATIVE_FIXNUM diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index d9c995662cf..05c78c06e5a 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -30,20 +30,20 @@ from cpython.object cimport Py_EQ, Py_NE #it would be preferrable to let bint_symbolp wrap an efficient macro #but the macro provided in object.h doesn't seem to work cdef bint bint_symbolp(cl_object obj): - return not(cl_symbolp(obj) == Cnil) + return not(cl_symbolp(obj) == ECL_NIL) #these type predicates are only provided in "cl_*" form, so we wrap them #with the proper type cast. cdef bint bint_numberp(cl_object obj): - return not(cl_numberp(obj) == Cnil) + return not(cl_numberp(obj) == ECL_NIL) cdef bint bint_integerp(cl_object obj): - return not(cl_integerp(obj) == Cnil) + return not(cl_integerp(obj) == ECL_NIL) cdef bint bint_rationalp(cl_object obj): - return not(cl_rationalp(obj) == Cnil) + return not(cl_rationalp(obj) == ECL_NIL) cdef bint bint_base_string_p(cl_object obj): - return not(si_base_string_p(obj) == Cnil) + return not(si_base_string_p(obj) == ECL_NIL) cdef extern from "eclsig.h": int ecl_sig_on() except 0 @@ -84,7 +84,7 @@ cdef cl_object insert_node_after(cl_object node,cl_object value): next=cl_cadr(node) newnode=cl_cons(value,cl_cons(next,node)) cl_rplaca(cl_cdr(node),newnode) - if next != Cnil: + if next != ECL_NIL: cl_rplacd(cl_cdr(next),newnode) return newnode @@ -92,9 +92,9 @@ cdef void remove_node(cl_object node): cdef cl_object next, prev next=cl_cadr(node) prev=cl_cddr(node) - if next != Cnil: + if next != ECL_NIL: cl_rplacd(cl_cdr(next),prev) - if prev != Cnil: + if prev != ECL_NIL: cl_rplaca(cl_cdr(prev),next) # our global list of pointers. This will be a pointer to a sentinel node, @@ -271,7 +271,7 @@ def init_ecl(): #initialise list of objects and bind to global variable # *SAGE-LIST-OF-OBJECTS* to make it rooted in the reachable tree for the GC - list_of_objects=cl_cons(Cnil,cl_cons(Cnil,Cnil)) + list_of_objects=cl_cons(ECL_NIL,cl_cons(ECL_NIL,ECL_NIL)) cl_set(string_to_object(b"*SAGE-LIST-OF-OBJECTS*"), list_of_objects) cl_eval(string_to_object(b""" @@ -406,7 +406,7 @@ def print_objects(): print(ecl_string_to_python(s)) c = cl_cadr(c) - if c == Cnil: + if c == ECL_NIL: break cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: @@ -422,11 +422,11 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: if isinstance(pyobj,bool): if pyobj: - return Ct + return ECL_T else: - return Cnil + return ECL_NIL elif pyobj is None: - return Cnil + return ECL_NIL elif isinstance(pyobj,long): if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) @@ -468,13 +468,13 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: elif isinstance(pyobj,EclObject): return (pyobj).obj elif isinstance(pyobj, list): - L = Cnil + L = ECL_NIL for i in range(len(pyobj)-1,-1,-1): L = cl_cons(python_to_ecl(pyobj[i], read_strings), L) return L elif isinstance(pyobj, tuple): if not pyobj: - return Cnil + return ECL_NIL else: L = python_to_ecl(pyobj[-1], read_strings) for i in range(len(pyobj)-2,-1,-1): @@ -489,7 +489,7 @@ cdef ecl_to_python(cl_object o): cdef Integer N # conversions from an ecl object to a python object. - if o == Cnil: + if o == ECL_NIL: return None elif bint_fixnump(o): # Sage specific conversion @@ -509,11 +509,11 @@ cdef ecl_to_python(cl_object o): # Python conversion # Since Sage mainly uses mpfr, perhaps "double is not an appropriate return type return ecl_to_double(o) - elif o == Ct: + elif o == ECL_T: return True elif bint_consp(o): L=[] - while o != Cnil: + while o != ECL_NIL: L.append(ecl_to_python(cl_car(o))) o = cl_cdr(o) if not(bint_listp(o)): @@ -1337,7 +1337,7 @@ cdef class EclListIterator: self.current = self.current.cdr() else: r = self.current - self.current = ecl_wrap(Cnil) + self.current = ecl_wrap(ECL_NIL) return r #input: a cl-object. Output: EclObject wrapping that. From cd63d838a25adbd8dbf61d7869e049c7e88cfe49 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Wed, 1 Sep 2021 09:13:31 +0200 Subject: [PATCH 011/122] libs: ecl: fix cl_boot invocation We shouldn't stack allocate this parameter because it becomes invalid once the scope leaves init_ecl, leading to crashes if `(ext:command-args)` is called in Lisp. --- src/sage/libs/ecl.pyx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 05c78c06e5a..fc641932969 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -109,6 +109,8 @@ cdef cl_object unicode_string_codepoints_clobj cdef bint ecl_has_booted = 0 +cdef char *argv = "sage" #we need a dummy argv for cl_boot (we just don't give any parameters) + # ECL signal handling def test_sigint_before_ecl_sig_on(): @@ -237,7 +239,7 @@ def init_ecl(): global read_from_string_clobj global conditions_to_handle_clobj global ecl_has_booted - cdef char *argv[1] + global argv cdef sigaction_t sage_action[32] cdef int i @@ -247,9 +249,6 @@ def init_ecl(): #we keep our own GMP memory functions. ECL should not claim them ecl_set_option(ECL_OPT_SET_GMP_MEMORY_FUNCTIONS,0); - #we need a dummy argv for cl_boot (we just don't give any parameters) - argv[0]="sage" - #get all the signal handlers before initializing Sage so we can #put them back afterwards. for i in range(1,32): @@ -257,7 +256,7 @@ def init_ecl(): #initialize ECL ecl_set_option(ECL_OPT_SIGNAL_HANDLING_THREAD, 0) - safe_cl_boot(1, argv) + safe_cl_boot(1, &argv) #save signal handler from ECL sigaction(SIGINT, NULL, &ecl_sigint_handler) From 0bdb8f13163501d6c82e392e02df0f29a953bdd4 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 10 Sep 2021 10:51:45 +0800 Subject: [PATCH 012/122] kernel polynomial is not used anymore in _richcmp_ --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index ebbd2d2fc0c..36876f5260e 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1228,10 +1228,11 @@ def __hash__(self): def _richcmp_(self, other, op): r""" - Function that implements comparisons between isogeny objects. + Compare :class:`EllipticCurveIsogeny` objects. - This function works by comparing the underlying kernel - objects. + ALGORITHM: + + This method compares domains, codomains, and :meth:`rational_maps`. EXAMPLES:: @@ -1256,9 +1257,6 @@ def _richcmp_(self, other, op): sage: phi.dual() == psi.dual() True """ - if (self.__kernel_polynomial is None): - self.__init_kernel_polynomial() - # We cannot just compare kernel polynomials, as was done until # Trac #11327, as then phi and -phi compare equal, and # similarly with phi and any composition of phi with an From ead071d9b09047a6f0d867ed9be21b85d0e9b854 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 8 Sep 2021 16:43:18 +0800 Subject: [PATCH 013/122] make sure multiplication_by_m_isogeny picks the correct isomorphism --- .../schemes/elliptic_curves/ell_generic.py | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 912496bca28..2c1300ed77a 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2150,7 +2150,7 @@ def multiplication_by_m(self, m, x_only=False): def multiplication_by_m_isogeny(self, m): r""" Return the ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on self. + multiplication-by-`m` map on this elliptic curve. The resulting isogeny will have the associated rational maps (i.e. those returned by @@ -2169,7 +2169,7 @@ def multiplication_by_m_isogeny(self, m): OUTPUT: - An ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on self. + multiplication-by-`m` map on this elliptic curve. EXAMPLES:: @@ -2177,6 +2177,29 @@ def multiplication_by_m_isogeny(self, m): sage: E.multiplication_by_m_isogeny(7) Isogeny of degree 49 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field + TESTS: + + Tests for :trac:`32490`:: + + sage: E = EllipticCurve(QQbar, [1,0]) + sage: E.multiplication_by_m_isogeny(1).rational_maps() + (x, y) + + :: + + sage: E = EllipticCurve_from_j(GF(31337).random_element()) + sage: P = E.random_point() + sage: [E.multiplication_by_m_isogeny(m)(P) == m*P for m in (1,2,3,5,7,9)] + [True, True, True, True, True, True] + + :: + + sage: E = EllipticCurve('99.a1') + sage: E.multiplication_by_m_isogeny(5) + Isogeny of degree 25 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field + sage: E.multiplication_by_m_isogeny(2).rational_maps() + ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), + (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) """ mx, my = self.multiplication_by_m(m) @@ -2184,7 +2207,13 @@ def multiplication_by_m_isogeny(self, m): phi = self.isogeny(torsion_poly, codomain=self) phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) - return phi + # trac 32490: using codomain=self can give a wrong isomorphism + for aut in self.automorphisms(): + psi = aut * phi + if psi.rational_maps() == (mx, my): + return psi + + assert False, 'bug in multiplication_by_m_isogeny()' def isomorphism_to(self, other): """ From b8bfd454514fa8251a9eb9e59b3d84c7f95f880e Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 23 Sep 2021 10:55:05 -0700 Subject: [PATCH 014/122] trac 32555: preface indented block in documentation with :: --- src/doc/en/developer/portability_testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst index 204c173dc52..fee2e78f882 100644 --- a/src/doc/en/developer/portability_testing.rst +++ b/src/doc/en/developer/portability_testing.rst @@ -707,7 +707,7 @@ other prerequisites installed in your system. See ``build/pkgs/_bootstrap/distros/*.txt`` for a list of system packages that provide these prerequisites. -We start by creating a fresh (distclean) git worktree. +We start by creating a fresh (distclean) git worktree:: [mkoeppe@sage sage] git worktree add worktree-local [mkoeppe@sage sage] cd worktree-local From 6633bc4ddd66e982fc3484f399cf62e50e36fd61 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 30 Sep 2021 23:32:44 +0800 Subject: [PATCH 015/122] use GSL macros for compatibility with gsl 2.7 --- src/sage/libs/gsl/complex.pxd | 6 +- src/sage/libs/gsl/types.pxd | 4 +- src/sage/plot/complex_plot.pyx | 7 +- src/sage/rings/complex_double.pyx | 71 ++++++++++--------- src/sage/rings/complex_mpfr.pyx | 6 +- src/sage/rings/convert/mpfi.pyx | 7 +- src/sage/symbolic/pynac_impl.pxi | 4 +- .../autogen/interpreters/specs/cdf.py | 6 +- 8 files changed, 58 insertions(+), 53 deletions(-) diff --git a/src/sage/libs/gsl/complex.pxd b/src/sage/libs/gsl/complex.pxd index d0e6f9f33a2..b6e0a16fbb1 100644 --- a/src/sage/libs/gsl/complex.pxd +++ b/src/sage/libs/gsl/complex.pxd @@ -10,11 +10,11 @@ cdef extern from "gsl/gsl_complex.h": int GSL_COMPLEX_EQ(gsl_complex z1,gsl_complex z2) - double GSL_SET_COMPLEX(gsl_complex * zp, double x, double y) + void GSL_SET_COMPLEX(gsl_complex * zp, double x, double y) - double GSL_SET_REAL(gsl_complex * zp, double x) + void GSL_SET_REAL(gsl_complex * zp, double x) - double GSL_SET_IMAG(gsl_complex * zp, double y) + void GSL_SET_IMAG(gsl_complex * zp, double y) cdef extern from "gsl/gsl_complex_math.h": diff --git a/src/sage/libs/gsl/types.pxd b/src/sage/libs/gsl/types.pxd index 04b9e17a501..217738be893 100644 --- a/src/sage/libs/gsl/types.pxd +++ b/src/sage/libs/gsl/types.pxd @@ -43,9 +43,7 @@ cdef extern from "gsl/gsl_complex.h": ctypedef double * gsl_complex_packed_ptr ctypedef struct gsl_complex: - double dat[2] - double real "dat[0]" - double imag "dat[1]" + pass cdef extern from "gsl/gsl_block_double.h": diff --git a/src/sage/plot/complex_plot.pyx b/src/sage/plot/complex_plot.pyx index d688b6e4996..acebaa95d62 100644 --- a/src/sage/plot/complex_plot.pyx +++ b/src/sage/plot/complex_plot.pyx @@ -29,11 +29,11 @@ from sage.arith.srange import srange from libc.math cimport hypot, atan2, atan, log, sqrt from sage.arith.constants cimport M_PI as PI +from sage.libs.gsl.complex cimport * cdef inline ComplexDoubleElement new_CDF_element(double x, double y): z = ComplexDoubleElement.__new__(ComplexDoubleElement) - z._complex.real = x - z._complex.imag = y + GSL_SET_COMPLEX(&z._complex, x, y) return z @@ -113,7 +113,8 @@ def complex_to_rgb(z_values): z = zz else: z = CDF(zz) - x, y = z._complex.dat + x = GSL_REAL(z._complex) + y = GSL_IMAG(z._complex) mag = hypot(x, y) arg = atan2(y, x) # math module arctan has range from -pi to pi, so cut along negative x-axis diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 3c47faf4344..432afdd5b1c 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -796,7 +796,7 @@ cdef class ComplexDoubleElement(FieldElement): True """ return (ComplexDoubleElement, - (self._complex.real, self._complex.imag)) + (GSL_REAL(self._complex), GSL_IMAG(self._complex))) cdef ComplexDoubleElement _new_c(self, gsl_complex x): """ @@ -861,13 +861,13 @@ cdef class ComplexDoubleElement(FieldElement): sage: 4.3 > CDF(5,1) False """ - if left._complex.real < (right)._complex.real: + if GSL_REAL(left._complex) < GSL_REAL((right)._complex): return rich_to_bool(op, -1) - if left._complex.real > (right)._complex.real: + if GSL_REAL(left._complex) > GSL_REAL((right)._complex): return rich_to_bool(op, 1) - if left._complex.imag < (right)._complex.imag: + if GSL_IMAG(left._complex) < GSL_IMAG((right)._complex): return rich_to_bool(op, -1) - if left._complex.imag > (right)._complex.imag: + if GSL_IMAG(left._complex) > GSL_IMAG((right)._complex): return rich_to_bool(op, 1) return rich_to_bool(op, 0) @@ -893,8 +893,10 @@ cdef class ComplexDoubleElement(FieldElement): ... IndexError: index n must be 0 or 1 """ - if n >= 0 and n <= 1: - return self._complex.dat[n] + if n == 0: + return GSL_REAL(self._complex) + if n == 1: + return GSL_IMAG(self._complex) raise IndexError("index n must be 0 or 1") def _magma_init_(self, magma): @@ -968,9 +970,9 @@ cdef class ComplexDoubleElement(FieldElement): sage: float(abs(CDF(1,1))) 1.4142135623730951 """ - if self._complex.imag: + if GSL_IMAG(self._complex): raise TypeError(f"unable to convert {self} to float; use abs() or real_part() as desired") - return self._complex.real + return GSL_REAL(self._complex) def __complex__(self): """ @@ -984,7 +986,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: complex(CDF(a)) (2303-3939j) """ - return complex(self._complex.real, self._complex.imag) + return complex(GSL_REAL(self._complex), GSL_IMAG(self._complex)) def _interface_init_(self, I=None): """ @@ -1042,7 +1044,8 @@ cdef class ComplexDoubleElement(FieldElement): sage: type(_) """ - x, y = self._complex.dat + x = GSL_REAL(self._complex) + y = GSL_IMAG(self._complex) import sympy return sympy.Float(x) + sympy.Float(y) * sympy.I @@ -1075,7 +1078,8 @@ cdef class ComplexDoubleElement(FieldElement): sage: CDF(0) 0.0 """ - x, y = self._complex.dat + x = GSL_REAL(self._complex) + y = GSL_IMAG(self._complex) if x == 0: if y == 0: # Not sure what to do with the signs of the real and @@ -1126,8 +1130,8 @@ cdef class ComplexDoubleElement(FieldElement): sage: format(CDF(0, 0), '+#.4') '+0.000' """ - return complex_mpfr._format_complex_number(self._complex.real, - self._complex.imag, + return complex_mpfr._format_complex_number(GSL_REAL(self._complex), + GSL_IMAG(self._complex), format_spec) def _latex_(self): @@ -1161,10 +1165,10 @@ cdef class ComplexDoubleElement(FieldElement): sage: pari(CDF(I)) 1.00000000000000*I """ - if not self._complex.imag: - return new_gen_from_double(self._complex.real) + if not GSL_IMAG(self._complex): + return new_gen_from_double(GSL_REAL(self._complex)) else: - return new_t_COMPLEX_from_double(self._complex.real, self._complex.imag) + return new_t_COMPLEX_from_double(GSL_REAL(self._complex), GSL_IMAG(self._complex)) def __mpc__(self): """ @@ -1179,7 +1183,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: mpc(c) mpc('2.0+1.0j') """ - return gmpy2.mpc(self._complex.dat[0], self._complex.dat[1]) + return gmpy2.mpc(GSL_REAL(self._complex), GSL_IMAG(self._complex)) ####################################################################### # Arithmetic @@ -1461,7 +1465,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: a.real_part() 3.0 """ - return RealDoubleElement(self._complex.real) + return RealDoubleElement(GSL_REAL(self._complex)) real_part = real @@ -1477,7 +1481,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: a.imag_part() -2.0 """ - return RealDoubleElement(self._complex.imag) + return RealDoubleElement(GSL_IMAG(self._complex)) imag_part = imag @@ -1718,9 +1722,9 @@ cdef class ComplexDoubleElement(FieldElement): res = gsl_complex_rect(1, 0) elif other == -1: res = gsl_complex_inverse(self._complex) - elif not self._complex.imag: + elif not GSL_IMAG(self._complex): # If self is real, the result should be real too - real = self._complex.real ** other + real = GSL_REAL(self._complex) ** other res = gsl_complex_rect(real, 0) else: # General case @@ -1728,9 +1732,9 @@ cdef class ComplexDoubleElement(FieldElement): return self._new_c(res) cpdef _pow_int(self, other): - if not self._complex.imag: + if not GSL_IMAG(self._complex): # If self is real, the result should be real too - real = self._complex.real ** other + real = GSL_REAL(self._complex) ** other res = gsl_complex_rect(real, 0) else: # General case @@ -2257,10 +2261,10 @@ cdef class ComplexDoubleElement(FieldElement): """ cdef GEN a, b, c, y, t - if self._complex.imag <= 0: + if GSL_IMAG(self._complex) <= 0: raise ValueError("value must be in the upper half plane") - if self._complex.imag > 100000 and not omit_frac: + if GSL_IMAG(self._complex) > 100000 and not omit_frac: # To the precision of doubles for such large imaginary # part, the answer is automatically 0. If we don't do # this, PARI can easily underflow. @@ -2400,13 +2404,13 @@ cdef class ComplexDoubleElement(FieldElement): sage: CDF(-1,0).gamma() Infinity """ - if not self._complex.imag: - if self._complex.real == 0: + if not GSL_IMAG(self._complex): + if GSL_REAL(self._complex) == 0: from .infinity import unsigned_infinity return unsigned_infinity try: from sage.rings.all import Integer, CC - if Integer(self._complex.real) < 0: + if Integer(GSL_REAL(self._complex)) < 0: return CC(self).gamma() except TypeError: pass @@ -2441,7 +2445,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: zeta(CDF(1)) Infinity """ - if self._complex.real == 1 and self._complex.imag == 0: + if GSL_REAL(self._complex) == 1 and GSL_IMAG(self._complex) == 0: from .infinity import unsigned_infinity return unsigned_infinity return pari_to_cdf(self.__pari__().zeta()) @@ -2633,8 +2637,8 @@ cdef inline double complex extract_double_complex(ComplexDoubleElement x): Return the value of ``x`` as a c99 complex double. """ cdef double complex z - z.real = x._complex.real - z.imag = x._complex.imag + z.real = GSL_REAL(x._complex) + z.imag = GSL_IMAG(x._complex) return z @@ -2644,6 +2648,5 @@ cdef inline ComplexDoubleElement ComplexDoubleElement_from_doubles(double re, do imaginary parts. """ z = ComplexDoubleElement.__new__(ComplexDoubleElement) - z._complex.real = re - z._complex.imag = im + GSL_SET_COMPLEX(&z._complex, re, im) return z diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index d121e29d537..00f1e5effd3 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -52,6 +52,7 @@ from .integer cimport Integer from .complex_double cimport ComplexDoubleElement from .real_mpfr cimport RealNumber +from sage.libs.gsl.complex cimport * from sage.libs.mpmath.utils cimport mpfr_to_mpfval from sage.rings.integer_ring import ZZ @@ -3439,8 +3440,9 @@ cdef class CCtoCDF(Map): 0.7071067811865476 + 0.7071067811865475*I """ z = ComplexDoubleElement.__new__(ComplexDoubleElement) - z._complex.real = mpfr_get_d((x).__re, MPFR_RNDN) - z._complex.imag = mpfr_get_d((x).__im, MPFR_RNDN) + GSL_SET_COMPLEX(&z._complex, + mpfr_get_d((x).__re, MPFR_RNDN), + mpfr_get_d((x).__im, MPFR_RNDN)) return z diff --git a/src/sage/rings/convert/mpfi.pyx b/src/sage/rings/convert/mpfi.pyx index a7d1561ec09..0e5e68c8825 100644 --- a/src/sage/rings/convert/mpfi.pyx +++ b/src/sage/rings/convert/mpfi.pyx @@ -16,6 +16,7 @@ from cpython.complex cimport PyComplex_RealAsDouble, PyComplex_ImagAsDouble from sage.libs.mpfr cimport * from sage.libs.mpfi cimport * +from sage.libs.gsl.complex cimport * from sage.arith.long cimport integer_check_long from sage.cpython.string cimport bytes_to_str @@ -135,11 +136,11 @@ cdef int mpfi_set_sage(mpfi_ptr re, mpfi_ptr im, x, field, int base) except -1: if isinstance(x, ComplexDoubleElement): zd = x if im is NULL: - if zd._complex.imag: + if GSL_IMAG(zd._complex): raise TypeError(f"unable to convert complex number {x!r} to real interval") else: - mpfi_set_d(im, zd._complex.imag) - mpfi_set_d(re, zd._complex.real) + mpfi_set_d(im, GSL_IMAG(zd._complex)) + mpfi_set_d(re, GSL_REAL(zd._complex)) return 0 else: # not a Sage Element # Real diff --git a/src/sage/symbolic/pynac_impl.pxi b/src/sage/symbolic/pynac_impl.pxi index 01a51af2997..f4ddecb577c 100644 --- a/src/sage/symbolic/pynac_impl.pxi +++ b/src/sage/symbolic/pynac_impl.pxi @@ -1734,7 +1734,7 @@ cdef py_log(x): return math.log(real) elif real < 0: res = gsl_complex_log(gsl_complex_rect(real, 0)) - return PyComplex_FromDoubles(res.real, res.imag) + return PyComplex_FromDoubles(GSL_REAL(res), GSL_IMAG(res)) else: return float('-inf') elif type(x) is complex: @@ -1743,7 +1743,7 @@ cdef py_log(x): if real == 0 and imag == 0: return float('-inf') res = gsl_complex_log(gsl_complex_rect(real, imag)) - return PyComplex_FromDoubles(res.real, res.imag) + return PyComplex_FromDoubles(GSL_REAL(res), GSL_IMAG(res)) elif isinstance(x, Integer): return x.log().n() elif hasattr(x, 'log'): diff --git a/src/sage_setup/autogen/interpreters/specs/cdf.py b/src/sage_setup/autogen/interpreters/specs/cdf.py index 1921c807c80..7f09c3b6a5c 100644 --- a/src/sage_setup/autogen/interpreters/specs/cdf.py +++ b/src/sage_setup/autogen/interpreters/specs/cdf.py @@ -157,6 +157,7 @@ def __init__(self): """) self.pyx_header = ri(0, """ + from sage.libs.gsl.complex cimport * from sage.rings.complex_double cimport ComplexDoubleElement import sage.rings.complex_double cdef object CDF = sage.rings.complex_double.CDF @@ -168,12 +169,11 @@ def __init__(self): cdef inline double_complex CDE_to_dz(zz): cdef ComplexDoubleElement z = (zz if isinstance(zz, ComplexDoubleElement) else CDF(zz)) - return z._complex.real + _Complex_I * z._complex.imag + return GSL_REAL(z._complex) + _Complex_I * GSL_IMAG(z._complex) cdef inline ComplexDoubleElement dz_to_CDE(double_complex dz): cdef ComplexDoubleElement z = ComplexDoubleElement.__new__(ComplexDoubleElement) - z._complex.real = creal(dz) - z._complex.imag = cimag(dz) + GSL_SET_COMPLEX(&z._complex, creal(dz), cimag(dz)) return z cdef public bint cdf_py_call_helper(object fn, From 875afbf83aaf1182902023e3d323feb4a855fd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 5 Dec 2014 20:12:13 +0100 Subject: [PATCH 016/122] trac #17442 italian translation of tutorial, start --- src/doc/it/tutorial/index.rst | 35 +++ src/doc/it/tutorial/introduction.rst | 146 ++++++++++ src/doc/it/tutorial/tour_algebra.rst | 398 +++++++++++++++++++++++++++ 3 files changed, 579 insertions(+) create mode 100644 src/doc/it/tutorial/index.rst create mode 100644 src/doc/it/tutorial/introduction.rst create mode 100644 src/doc/it/tutorial/tour_algebra.rst diff --git a/src/doc/it/tutorial/index.rst b/src/doc/it/tutorial/index.rst new file mode 100644 index 00000000000..e192767dd6f --- /dev/null +++ b/src/doc/it/tutorial/index.rst @@ -0,0 +1,35 @@ +.. Sage documentation master file, created by sphinx-quickstart on Thu Aug 21 20:15:55 2008. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Benvenuti al tutorial di Sage! +================================ + +Sage è un software di calcolo numerico e simbolico libero e open-source di supporto +nell'insegnamento e della ricerca in algebra, geometria, teoria dei numeri, crittografia, +calcolo numerico e aree correlate. Sia il modello di sviluppo di Sage, sia la tecnologia +utilizzata in Sage stesso, si distinguono per un'enfasi particolarmente forte +su apertura, community, cooperazione e collaborazione: vogliamo creare +l'automobile, non reinventare la ruota. L'obiettivo complessivo di Sage è creare +un'alternativa adeguata, libera e open-source a Maple, Mathematica, Magma e +MATLAB. + +Questo tutorial è la via migliore per familiarizzare con Sage in poche ore e può +essere letto in HTML o PDF, oppure direttamente dal Notebook di Sage (fare click +su "Help" e poi su "Tutorial" per seguire il tutorial in modo interattivo +dall'interno di Sage). + + +.. toctree:: + :maxdepth: 2 + + introduction + tour_algebra + +Indici e tabelle +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`cerca` + diff --git a/src/doc/it/tutorial/introduction.rst b/src/doc/it/tutorial/introduction.rst new file mode 100644 index 00000000000..e72e4ef25e1 --- /dev/null +++ b/src/doc/it/tutorial/introduction.rst @@ -0,0 +1,146 @@ +************ +Introduzione +************ +Questo tutorial dovrebbe richiedere circa 3/4 ore per +una lettura completa. Lo si può leggere in versione HTML o PDF, o dal notebook Sage; +fare clic su "Help", poi fare clic su "Tutorial" per leggere interattivamente +il tutorial dall'interno di Sage. + +Nonostante molto in Sage sia implementato usando Python, la conoscenza di Python +non è un prerequisito per la lettura di questo tutorial. Per chi volesse imparare +il Python (un linguaggio molto divertente!) allo stesso tempo, ci sono molte risorse +eccellenti e libere per farlo tra le quali [PyT]_ e [Dive]_. +Se si vuole solo provare velocemente Sage, questo tutorial è il punto di partenza adatto. +Per esempio: + +:: + + sage: 2 + 2 + 4 + sage: factor(-2007) + -1 * 3^2 * 223 + + sage: A = matrix(4,4, range(16)); A + [ 0 1 2 3] + [ 4 5 6 7] + [ 8 9 10 11] + [12 13 14 15] + + sage: factor(A.charpoly()) + x^2 * (x^2 - 30*x - 80) + + sage: m = matrix(ZZ,2, range(4)) + sage: m[0,0] = m[0,0] - 3 + sage: m + [-3 1] + [ 2 3] + + sage: E = EllipticCurve([1,2,3,4,5]); + sage: E + Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 + over Rational Field + sage: E.anlist(10) + [0, 1, 1, 0, -1, -3, 0, -1, -3, -3, -3] + sage: E.rank() + 1 + + sage: k = 1/(sqrt(3)*I + 3/4 + sqrt(73)*5/9); k + 36/(20*sqrt(73) + 36*I*sqrt(3) + 27) + + sage: N(k) + 0.165495678130644 - 0.0521492082074256*I + sage: N(k,30) # 30 "bits" + 0.16549568 - 0.052149208*I + sage: latex(k) + \frac{36}{20 \, \sqrt{73} + 36 i \, \sqrt{3} + 27} + +Installazione +============= + +Se non si ha Sage installato su un computer e si vogliono solamente +provare alcuni comandi, si può usare online all'indirizzo http://www.sagenb.org. + +Si veda la Sage Installation Guide nella sezione documentazione della homepage +di Sage [Sage]_ per istruzioni sull'installazione di Sage sul proprio computer. +Qui vengono fatti solamente due commenti. + + +#. Il file di download di Sage arrive con le "batterie incluse". + In altre parole, nonostante Sage usi Python, IPython, PARI, GAP, + Singular, Maxima, NTL, GMP e così via, non è necessario installarli + separatemente siccome sono incluse con la distribuzione di Sage. + Comunque, per usare certe feature di \sage, ad es. Macaulay o KASH, + bisogna installare il pacchetto opzionale Sage che interessa o almeno + avere i programmi in questioni gia installati sul proprio computer. + Macaulay e KASH sono pacchetti di Sage (per una lista dei pacchetti + opzionali disponibili, digitare "sage -optional", o sfogliare la pagina + "Download" sul sito web di Sage. + +#. Le versioni binarie precompilate di Sage (che si trovano sul sito web di + Sage) possono essere più facili e più veloci da installare invece che la + versione da codice sorgente. Basta solo spachettare il file e eseguire "sage". + +Modi di usare Sage +================== + +Sage si può usare in molti modi. + + +- **Interfaccia grafica del notebook:** vedere la sezioni sul + Notebook nel manuale di riferimento e la :ref:"sezione-notebook" sotto, + +- **Linea di comando interattiva:** vedere :ref:'capitolo-shell_interattiva', + +- **Programmi:** scrivendo programmi interpretati e compilati in Sage (vedere + :ref:'sezione-loadattach' e :ref:'sezione-compilazione'), e + +- **Scripts:** scrivendo degli script autosufficienti che usino la libreria + Sage (vedere :ref:'sezione-autosufficienti'). + + +Obiettivi di lungo periodo per Sage +=================================== + +- **Utile**: il pubblico per Sage il quale sage è stato pensato sono gli + studentu di matematica (dalla scuola superiore all'università), gli insegnanti + e i ricercatori in matematica. Lo scopo è di fornire software che possa essere + usato per esplorare e sperimentare le costruzioni matematiche in algebra, + geometria, teoria dei numeri, calcolo, calcolo numerico, ecc. Sage aiuta a + rendere più facile la sperimentazione interattiva con gli oggetti matematici. + +- **Efficiente:** essere veloce. Sage usa del software maturo e altamente + ottimizzato come GMP, PARI, GAP e NTL e così è molto veloce con certe + operazioni. + +- **Libero e open source:** il codice sorgente deve essere liberamente disponibile + e leggibile, così che gli utenti posssano capire cosa stia facendo veramente il + sistema e possano estenderlo più facilmente. Così come i matematici acquisiscono + una comprensione più profonda di un teorema leggendo attentamete o almeno scorrendo + velocemente la dimostrazione, le persone che fanno calcoli dovrebbero essere capaci + di capire come funzionano i calcoli leggengo il codice sorgente documentato. Se + si usa Sage per fare calcoli in un articolo che si pubblica, si può essere rassicurati + dal fatto che i lettori avranno sempre libero accesso a Sage e a tutto il suo codice + sorgente ed è persino concesso di archiviare la versione di Sage che si è utilizzata. + +- **Facile da compilare:** Sage dovrebbe essere facile da compilare dal sorgente per + gli utenti Linux, OS X e Windows. Questo garantisce maggiore flessibilità agli utenti + di modificare il sistema. + +- **Cooperazione:** Fornire un interfaccia robusta alla maggior parte degli altri sistemi + di algebra computazionale, compresi: PARI, GAP, Singular, Maxima, KASH, Magma, Maple e + Mathematica. Sage è pensato per unificare e estendere il software matematico esistente. + +- **Ben documentato:** tutorial, guida alla programmazione, manuale di riferimento e + how to con numerosi esempi e discussioni della matematica sottostante. + +- **Amichevole verso l'utente:** dovrebbe essere facile capire quale funzionalità è + fornita per un dato oggetto e guardare la documentazione e il codice sorgente. + Bisogna anche raggiungere un alto livello di supporto agli utenti. + + +.. [Dive] (en) Tuffati in Python, Liberamente disponibile in linea + all'indirizzo: http://www.diveintopython.net + +.. [PyT] (en) Il tutorial Python, http://www.python.org/ + +.. [Sage] (en) Sage, http://www.sagemath.org diff --git a/src/doc/it/tutorial/tour_algebra.rst b/src/doc/it/tutorial/tour_algebra.rst new file mode 100644 index 00000000000..7ef0b3d799d --- /dev/null +++ b/src/doc/it/tutorial/tour_algebra.rst @@ -0,0 +1,398 @@ +Algebra di base e Analisi + +========================= + +Sage sa svolgere diversi calcoli legati all'algebra di base +ed all'analisi: per esempio, risoluzione di equazioni, +calcolo differenziale ed integrale e trasformate di Laplace. +Si veda la documentazione per le "Costruzioni di Sage" per +ulteriori esempi. + +Risoluzione di equazioni +------------------------ + +La funzione ``solve`` risolve le equazioni. Per usarla, +bisogna anzitutto specificare alcune variabili; pertanto +gli argomenti di ``solve`` sono un'equazione (od un sistema +di equazioni), insieme con le variabili rispetto alle quali +risolvere: + +:: + + sage: x = var('x') + sage: solve(x^2 + 3*x + 2, x) + [x == -2, x == -1] + +Si possono risolvere le equazioni rispetto ad una variabile in funzione +delle altre: + +:: + + sage: x, b, c = var('x b c') + sage: solve([x^2 + b*x + c == 0],x) + [x == -1/2*b - 1/2*sqrt(b^2 - 4*c), x == -1/2*b + 1/2*sqrt(b^2 - 4*c)] + +Si può anche risolvere rispetto a diverse variabili: + +:: + + sage: x, y = var('x, y') + sage: solve([x+y==6, x-y==4], x, y) + [[x == 5, y == 1]] + +Il seguente esempio dell'uso di Sage per risolvere un sistema di +equazioni non lineari è stato fornito da Jason Grout: per prima cosa, +si risolve il sistema simbolicamente: + +:: + + sage: var('x y p q') + (x, y, p, q) + sage: eq1 = p+q==9 + sage: eq2 = q*y+p*x==-6 + sage: eq3 = q*y^2+p*x^2==24 + sage: solve([eq1,eq2,eq3,p==1],p,q,x,y) + [[p == 1, q == 8, x == -4/3*sqrt(10) - 2/3, y == 1/6*sqrt(10) - 2/3], + [p == 1, q == 8, x == 4/3*sqrt(10) - 2/3, y == -1/6*sqrt(10) - 2/3]] + +Per una soluzione numerica, si può invece usare: + +.. link + +:: + + sage: solns = solve([eq1,eq2,eq3,p==1],p,q,x,y, solution_dict=True) + sage: [[s[p].n(30), s[q].n(30), s[x].n(30), s[y].n(30)] for s in solns] + [[1.0000000, 8.0000000, -4.8830369, -0.13962039], + [1.0000000, 8.0000000, 3.5497035, -1.1937129]] + +(La funzione ``n`` scrive un'approssimazione numerica, e +l'argomento è il numero di bit di precisione.) + +Differenziazione, Integrazione, etc. +------------------------------------ + +Sage è in grado di differenziae ed integrare molte funzioni. Per +esempio, per differenziare :math:`\sin(u)` rispetto a :math:`u`, +si procede come nelle righe seguenti: + +:: + + sage: u = var('u') + sage: diff(sin(u), u) + cos(u) + +Per calcolare la derivata quarta di :math:`\sin(x^2)`: + +:: + + sage: diff(sin(x^2), x, 4) + 16*x^4*sin(x^2) - 48*x^2*cos(x^2) - 12*sin(x^2) + +Per calcolare le derivate parziali di :math:`x^2+17y^2` +rispetto a *x* e *y*, rispettivamente: + +:: + + sage: x, y = var('x,y') + sage: f = x^2 + 17*y^2 + sage: f.diff(x) + 2*x + sage: f.diff(y) + 34*y + +Passiamo agli integrali, sia indefiniti che definiti. Per calcolare +:math:`\int x\sin(x^2)\, dx` e +:math:`\int_0^1 \frac{x}{x^2+1}\, dx` + +:: + + sage: integral(x*sin(x^2), x) + -1/2*cos(x^2) + sage: integral(x/(x^2+1), x, 0, 1) + 1/2*log(2) + +Per calcolare la decomposizione in frazioni parziali di +:math:`\frac{1}{x^2-1}`: + +:: + + sage: f = 1/((1+x)*(x-1)) + sage: f.partial_fraction(x) + -1/2/(x + 1) + 1/2/(x - 1) + +.. _section-systems: + +Risoluzione di Equazioni Differenziali +-------------------------------------- + +Si può usare Sage per studiare le equazioni differenziali ordinarie. +Per risolvere l'equazione :math:`x'+x-1=0`: + +:: + + sage: t = var('t') # definisce una variabile t + sage: x = function('x')(t) # definisce x come funzione di quella variabile + sage: DE = diff(x,t) + x - 1 + sage: desolve(DE, [x,t]) + (_C + e^t)*e^(-t) + +Questo metodo utilizza l'interfaccia di Sage per Maxima [Max]_, e così il suo +output può essere leggermente diverso dagli altri output di Sage. In questo caso, +risulta che la soluzione generale dell'equazione differenziale è +:math:`x(t) = e^{-t}(e^{t}+c)`. + +Si può anche calcolare la trasformata di Laplace; la trasformata di Laplace di +:math:`t^2e^t -\sin(t)` è calcolata come segue: + +:: + + sage: s = var("s") + sage: t = var("t") + sage: f = t^2*exp(t) - sin(t) + sage: f.laplace(t,s) + -1/(s^2 + 1) + 2/(s - 1)^3 + +Il successivo è un esempio più articolato. Lo scostamento dall'equilibrio +(rispettivamente) per due molle accoppiate fissate ad un muro a sinistra + +:: + + |------\/\/\/\/\---|massa1|----\/\/\/\/\/----|massa2| + molla1 molla2 + +è modellizzato dal sistema di equazioni differenziali del secondo ordine + +.. math:: + m_1 x_1'' + (k_1+k_2) x_1 - k_2 x_2 = 0 + m_2 x_2''+ k_2 (x_2-x_1) = 0, + + + +dove :math:`m_{i}` è la massa dell'oggetto *i*, :math:`x_{i}` è +lo scostamento dall'equilibrio della massa *i*, e :math:`k_{i}` +è la costante elastica della molla *i*. + +**Esempio:** Usare Sage per risolvere il problema precedente con +:math:`m_{1}=2`, :math:`m_{2}=1`, :math:`k_{1}=4`, +:math:`k_{2}=2`, :math:`x_{1}(0)=3`, :math:`x_{1}'(0)=0`, +:math:`x_{2}(0)=3`, :math:`x_{2}'(0)=0`. + +Soluzione: Calcolare la trasformata di Laplace della prima equazione (con +la notazione :math:`x=x_{1}`, :math:`y=x_{2}`: + +:: + + sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") + sage: lde1 = de1.laplace("t","s"); lde1 + 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + +Questo è di difficile lettura, ma dice che + +.. math:: -2x'(0) + 2s^2*X(s) - 2sx(0) - 2Y(s) + 6X(s) = 0 + + +(dove la trasformata di Laplace di una funzione in minuscolo come +:math:`x(t)` è la funzione in maiuscolo :math:`X(s)`). Calcolare la +trasformata di Laplace della seconda equazione: + +:: + + sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") + sage: lde2 = de2.laplace("t","s"); lde2 + (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + +che significa + +.. math:: -Y'(0) + s^2Y(s) + 2Y(s) - 2X(s) - sy(0) = 0. + + +Imporre le condizioni iniziali per :math:`x(0)`, :math:`x'(0)`, +:math:`y(0)`, e :math:`y'(0)`, e risolvere le due equazioni +risultanti: + +:: + + sage: var('s X Y') + (s, X, Y) + sage: eqns = [(2*s^2+6)*X-2*Y == 6*s, -2*X +(s^2+2)*Y == 3*s] + sage: solve(eqns, X,Y) + [[X == 3*(s^3 + 3*s)/(s^4 + 5*s^2 + 4), + Y == 3*(s^3 + 5*s)/(s^4 + 5*s^2 + 4)]] + +Ora si calcola la trasformata inversa di Laplace per ottenere la risposta: + +:: + + sage: var('s t') + (s, t) + sage: inverse_laplace((3*s^3 + 9*s)/(s^4 + 5*s^2 + 4),s,t) + cos(2*t) + 2*cos(t) + sage: inverse_laplace((3*s^3 + 15*s)/(s^4 + 5*s^2 + 4),s,t) + -cos(2*t) + 4*cos(t) + +Pertanto, la soluzione è + +.. math:: x_1(t) = \cos(2t) + 2\cos(t), \quad x_2(t) = 4\cos(t) - \cos(2t). + + +Essa può essere disegnata in forma parametrica usando + +:: + + sage: t = var('t') + sage: P = parametric_plot((cos(2*t) + 2*cos(t), 4*cos(t) - cos(2*t) ), + ....: (0, 2*pi), rgbcolor=hue(0.9)) + sage: show(P) + +Le singole componenti possono essere tracciate usando: + +:: + + sage: t = var('t') + sage: p1 = plot(cos(2*t) + 2*cos(t), 0, 2*pi, rgbcolor=hue(0.3)) + sage: p2 = plot(4*cos(t) - cos(2*t), 0, 2*pi, rgbcolor=hue(0.6)) + sage: show(p1 + p2) + +(Per ulteriori informazioni sul disegno di funzioni, si veda :ref:`section-plot`.) + +BIBLIOGRAFIA: Nagle, Saff, Snider, Fundamentals of Differential +Equations, 6th ed, Addison-Wesley, 2004. (si veda § 5.5). + +Metodo di Eulero per i sistemi di equazioni differenziali +--------------------------------------------------------- + +Nel prossimo esempio, si illustrerà il metodo di Eulero per le ODE +di primo e secondo ordine. Per prima cosa ricordiamo l'idea di base per +le equazioni di primo ordine. Dato un problema di Cauchy della forma + +.. math:: + y'=f(x,y) + y(a)=c + + +si vuole trovare il valore approssimato della soluzione a +:math:`x=b` con :math:`b>a`. + +Ricordando dalla definizione di derivata che + +.. math:: y'(x) \approx \frac{y(x+h)-y(x)}{h}, + + +dove :math:`h>0` è dato e piccolo. Questo e la DE insieme danno +give :math:`f(x,y(x))\approx +\frac{y(x+h)-y(x)}{h}`. Ora si risolve +per :math:`y(x+h)`: + +.. math:: y(x+h) \approx y(x) + h*f(x,y(x)). + + +Se chiamiamo :math:`h f(x,y(x))` il "termine di correzione" (per mancanza +di un termine migliore), :math:`y(x)` il "vecchio valore di *y*", e + :math:`y(x+h)` il "nuovo valore di *y*", allora questa +approssimazione può essere espressa come + +.. math:: y_{new} \approx y_{old} + h*f(x,y_{old}). + + +Se si spezza l'intervallo da *a* a *b* in *n* intervalli, dimodoché +:math:`h=\frac{b-a}{n}`, allora si possono registrare le informazioni per +questo metodo in una tabella. + +============== ================== ================ +:math:`x` :math:`y` :math:`hf(x,y)` +============== ================== ================ +:math:`a` :math:`c` :math:`hf(a,c)` +:math:`a+h` :math:`c+hf(a,c)` ... +:math:`a+2h` ... +... +:math:`b=a+nh` ??? ... +============== ================== ================ + + +L'obiettivo è riempire tutti gli spazi vuoti della tavella, una riga alla +volta, finché si arriva al valore ???, che è il +metodo di approssimazione di Eulero per :math:`y(b)`. + +L'idea per sistemi di ODE è simile. + +**Esempio:** Si approssimi numericamente :math:`z(t)` a :math:`t=1` usando 4 +passi del metodo di Eulero, dove :math:`z''+tz'+z=0`, +:math:`z(0)=1`, :math:`z'(0)=0`. + +Si deve ridurre l'ODE di secondo ordine ad un sistema di due equazioni del primo +ordine (usando :math:`x=z`, :math:`y=z'`) ed applicare il metodo di +Eulero: + +:: + + sage: t,x,y = PolynomialRing(RealField(10),3,"txy").gens() + sage: f = y; g = -x - y * t + sage: eulers_method_2x2(f,g, 0, 1, 0, 1/4, 1) + t x h*f(t,x,y) y h*g(t,x,y) + 0 1 0.00 0 -0.25 + 1/4 1.0 -0.062 -0.25 -0.23 + 1/2 0.94 -0.12 -0.48 -0.17 + 3/4 0.82 -0.16 -0.66 -0.081 + 1 0.65 -0.18 -0.74 0.022 + +Pertanto, :math:`z(1)\approx 0.75`. + +Si possono anche tracciare i punti :math:`(x,y)` per ottenere un grafico +approssimato della curva. La funzione ``eulers_method_2x2_plot`` svolge +questa funzione; per usarla, bisogna definire le funzioni *f* e +*g* che prendono on argomento con tre coordinate: (*t*, *x*, +*y*). + +:: + + sage: f = lambda z: z[2] # f(t,x,y) = y + sage: g = lambda z: -sin(z[1]) # g(t,x,y) = -sin(x) + sage: P = eulers_method_2x2_plot(f,g, 0.0, 0.75, 0.0, 0.1, 1.0) + +A questo punto, ``P`` ha in memoria due grafici: ``P[0]``, il grafico di *x* +vs. *t*, e ``P[1]``, il grafico di *y* vs. *t*. Si possono tracciare entrambi +come mostrato qui in seguito: + +.. link + +:: + + sage: show(P[0] + P[1]) + +(Per ulteriori informazioni sul disegno di grafici, si veda :ref:`section-plot`.) + +Funzioni speciali +----------------- + +Sono implementati diversi polinomi ortogonali e funzioni +speciali, usando sia PARI [GAP]_ che Maxima [Max]_. Essi +sono documentati nelle sezioni apposite ("Polinomi ortogonali" +e "Funzioni speciali", rispettivamente) del manuale di Sage. + +:: + + sage: x = polygen(QQ, 'x') + sage: chebyshev_U(2,x) + 4*x^2 - 1 + sage: bessel_I(1,1).n(250) + 0.56515910399248502720769602760986330732889962162109200948029448947925564096 + sage: bessel_I(1,1).n() + 0.565159103992485 + sage: bessel_I(2,1.1).n() + 0.167089499251049 + +A questo punto, Sage ha soltanto incorporato queste funzioni per l'uso numerico. +Per l'uso simbolico, si usi direttamente l'intefaccia di Maxima, come +nell'esempio seguente: + +:: + + sage: maxima.eval("f:bessel_y(v, w)") + 'bessel_y(v,w)' + sage: maxima.eval("diff(f,w)") + '(bessel_y(v-1,w)-bessel_y(v+1,w))/2' + +.. [GAP] (en) The GAP Group, ``GAP - Groups, Algorithms, and Programming``, http://www.gap-system.org + +.. [Max] (en) Maxima, http://maxima.sf.net/ From a5c3ea8e7bbbefdd9b7ac65e70af4d6fc88c11e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 3 Oct 2021 10:54:45 +0200 Subject: [PATCH 017/122] fixing some details, refresh --- src/doc/it/tutorial/index.rst | 5 +---- src/doc/it/tutorial/introduction.rst | 19 +++++++++---------- src/doc/it/tutorial/tour_algebra.rst | 5 +++-- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/doc/it/tutorial/index.rst b/src/doc/it/tutorial/index.rst index e192767dd6f..c4c4ade017a 100644 --- a/src/doc/it/tutorial/index.rst +++ b/src/doc/it/tutorial/index.rst @@ -15,9 +15,7 @@ un'alternativa adeguata, libera e open-source a Maple, Mathematica, Magma e MATLAB. Questo tutorial è la via migliore per familiarizzare con Sage in poche ore e può -essere letto in HTML o PDF, oppure direttamente dal Notebook di Sage (fare click -su "Help" e poi su "Tutorial" per seguire il tutorial in modo interattivo -dall'interno di Sage). +essere letto in HTML o PDF. .. toctree:: @@ -32,4 +30,3 @@ Indici e tabelle * :ref:`genindex` * :ref:`modindex` * :ref:`cerca` - diff --git a/src/doc/it/tutorial/introduction.rst b/src/doc/it/tutorial/introduction.rst index e72e4ef25e1..7e388e04dfc 100644 --- a/src/doc/it/tutorial/introduction.rst +++ b/src/doc/it/tutorial/introduction.rst @@ -2,14 +2,12 @@ Introduzione ************ Questo tutorial dovrebbe richiedere circa 3/4 ore per -una lettura completa. Lo si può leggere in versione HTML o PDF, o dal notebook Sage; -fare clic su "Help", poi fare clic su "Tutorial" per leggere interattivamente -il tutorial dall'interno di Sage. +una lettura completa. Lo si può leggere in versione HTML o PDF. Nonostante molto in Sage sia implementato usando Python, la conoscenza di Python non è un prerequisito per la lettura di questo tutorial. Per chi volesse imparare il Python (un linguaggio molto divertente!) allo stesso tempo, ci sono molte risorse -eccellenti e libere per farlo tra le quali [PyT]_ e [Dive]_. +eccellenti e libere per farlo tra le quali [PyT]_ e [PyB]_. Se si vuole solo provare velocemente Sage, questo tutorial è il punto di partenza adatto. Per esempio: @@ -58,7 +56,7 @@ Installazione ============= Se non si ha Sage installato su un computer e si vogliono solamente -provare alcuni comandi, si può usare online all'indirizzo http://www.sagenb.org. +provare alcuni comandi, si può usare online all'indirizzo https://sagecell.sagemath.org/. Si veda la Sage Installation Guide nella sezione documentazione della homepage di Sage [Sage]_ per istruzioni sull'installazione di Sage sul proprio computer. @@ -123,7 +121,7 @@ Obiettivi di lungo periodo per Sage sorgente ed è persino concesso di archiviare la versione di Sage che si è utilizzata. - **Facile da compilare:** Sage dovrebbe essere facile da compilare dal sorgente per - gli utenti Linux, OS X e Windows. Questo garantisce maggiore flessibilità agli utenti + gli utenti Linux, macOS e Windows. Questo garantisce maggiore flessibilità agli utenti di modificare il sistema. - **Cooperazione:** Fornire un interfaccia robusta alla maggior parte degli altri sistemi @@ -138,9 +136,10 @@ Obiettivi di lungo periodo per Sage Bisogna anche raggiungere un alto livello di supporto agli utenti. -.. [Dive] (en) Tuffati in Python, Liberamente disponibile in linea - all'indirizzo: http://www.diveintopython.net +.. [PyB] (en) The Python Beginner's Guide, + https://wiki.python.org/moin/BeginnersGuide -.. [PyT] (en) Il tutorial Python, http://www.python.org/ +.. [PyT] (en) The Python Tutorial, + https://docs.python.org/3/tutorial/ -.. [Sage] (en) Sage, http://www.sagemath.org +.. [Sage] (en) Sage, https://www.sagemath.org diff --git a/src/doc/it/tutorial/tour_algebra.rst b/src/doc/it/tutorial/tour_algebra.rst index 7ef0b3d799d..c745905ab05 100644 --- a/src/doc/it/tutorial/tour_algebra.rst +++ b/src/doc/it/tutorial/tour_algebra.rst @@ -393,6 +393,7 @@ nell'esempio seguente: sage: maxima.eval("diff(f,w)") '(bessel_y(v-1,w)-bessel_y(v+1,w))/2' -.. [GAP] (en) The GAP Group, ``GAP - Groups, Algorithms, and Programming``, http://www.gap-system.org +.. [GAP] (en) The GAP Group, GAP - Groups, Algorithms, and + Programming, Version 4.11; 2021, https://www.gap-system.org -.. [Max] (en) Maxima, http://maxima.sf.net/ +.. [Max] (en) Maxima, Version 5.45; 2021, http://maxima.sf.net/ From 6dfcfd9517f7a3d7ee5aa58ac80b29464f3c1248 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 5 Oct 2021 22:32:00 +0800 Subject: [PATCH 018/122] fix formula for Weierstrass isomorphisms when char=2, j!=0 --- .../elliptic_curves/weierstrass_morphism.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index f55123c70ea..39ea09acb3d 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -282,6 +282,19 @@ def isomorphisms(E, F, JustOne=False): sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1')) [] sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'),JustOne=True) + + TESTS: + + Check that :trac:`32632` is fixed:: + + sage: z8 = GF(2^8).gen() + sage: E1 = EllipticCurve([z8, z8, z8, z8, z8]) + sage: isomorphisms(E1, E1) + [(1, 0, 0, 0), (1, 0, z8, z8)] + sage: E2 = EllipticCurve([z8^2, 0, 0, 0, z8^7 + z8^4]) + sage: isomorphisms(E1, E2) + [(z8^7 + z8^3 + z8^2 + z8, 1, 1, z8^7 + z8^3 + z8^2 + z8 + 1), + (z8^7 + z8^3 + z8^2 + z8, 1, z8 + 1, z8^7 + z8^3 + z8^2 + z8 + 1)] """ from .ell_generic import is_EllipticCurve if not is_EllipticCurve(E) or not is_EllipticCurve(F): @@ -325,7 +338,7 @@ def isomorphisms(E, F, JustOne=False): r = (a3E+a3F*u**3)/a1E slist = [s[0] for s in (x**2+a1E*x+(r+a2E+a2F*u**2)).roots()] for s in slist: - t = (a4E+a4F*u**4 + s*a3E + r*s*a1E + r**2) + t = (a4E+a4F*u**4 + s*a3E + r*s*a1E + r**2) / a1E if JustOne: return (u, r, s, t) ans.append((u, r, s, t)) From 070eaafcbec4304630b6a1f5864f293c14fd0b8c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:31:13 -0400 Subject: [PATCH 019/122] Trac #32656: don't list psutil as a dependency of pyrsistent. Perusing the pyrsistent source code, we see that the psutil package is needed only to run the pyrsistent test suite -- not in the course of normal usage. Here we remove "psutil" from its list of dependencies, pending the removal of psutil itself, so that `git grep psutil` does not return false positives. --- build/pkgs/pyrsistent/SPKG.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/build/pkgs/pyrsistent/SPKG.rst b/build/pkgs/pyrsistent/SPKG.rst index a0209e2dfdd..d943e31aaf8 100644 --- a/build/pkgs/pyrsistent/SPKG.rst +++ b/build/pkgs/pyrsistent/SPKG.rst @@ -25,7 +25,6 @@ Dependencies - Setuptools - hypothesis - memory-profiler -- psutil - pyperform - pytest - Sphinx From f3a720181e95019ffafc8d0f84f7f1be88a2ae92 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:41:06 -0400 Subject: [PATCH 020/122] Trac #32656: remove commented GAP code in quadratic_form__neighbors.py. The sage/quadratic_forms/quadratic_form__neighbors.py file contains some commented code for messing with the GAP memory pool size. The code is of course never run, but since we intend to remove the GAP memory management code altogether in a future commit, now is as good a time as any to prune these dead comments. --- src/sage/quadratic_forms/quadratic_form__neighbors.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/quadratic_forms/quadratic_form__neighbors.py b/src/sage/quadratic_forms/quadratic_form__neighbors.py index 15c31fb8d91..1a6cf3be1c4 100644 --- a/src/sage/quadratic_forms/quadratic_form__neighbors.py +++ b/src/sage/quadratic_forms/quadratic_form__neighbors.py @@ -394,12 +394,7 @@ def orbits_lines_mod_p(self, p): reps:= List(orb, g->g[1]); return reps; end;""") - # run this at startup if you need more memory... - #from sage.interfaces.gap import get_gap_memory_pool_size, set_gap_memory_pool_size - #memory_gap = get_gap_memory_pool_size() - #set_gap_memory_pool_size(1028*memory_gap) orbs_reps = orbs(gens, p) - #set_gap_memory_pool_size(memory_gap) M = GF(p)**self.dim() return [M(m.sage()) for m in orbs_reps if not m.IsZero()] From 44f7442085fd21b4a63ff4979bb5d23f87459158 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:51:35 -0400 Subject: [PATCH 021/122] Trac #32656: remove unused import in sage.matrix.matrix_integer_dense. This module imports get_memory_usage() from sage.misc.getusage, but then never calls it. We plan to remove these memory-management functions soon, so we prune the unused import now. --- src/sage/matrix/matrix_integer_dense.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 5422d985eee..1c0a7e08997 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -4873,7 +4873,6 @@ cdef class Matrix_integer_dense(Matrix_dense): [ 0 0 545], [0, 1, 2] ) """ - from sage.misc.getusage import get_memory_usage cdef Py_ssize_t i, j, piv, n = self._nrows, m = self._ncols from .constructor import matrix From 1bcc8c92aba162b703c0fa5bb2fd7eb3137a6e0d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:53:29 -0400 Subject: [PATCH 022/122] Trac #32656: remove unused sage/modular/modform/test.py. This file contains a bunch of vestigial modular forms tests that were added to the tree in 2008, and which would (if ever run) make use of sage.misc.getusage. Since this file essentially does nothing, we remove it now so that e.g. `git grep getusage` does not show false positives prior to the removal of that module. --- src/sage/modular/modform/test.py | 125 ------------------------------- 1 file changed, 125 deletions(-) delete mode 100644 src/sage/modular/modform/test.py diff --git a/src/sage/modular/modform/test.py b/src/sage/modular/modform/test.py deleted file mode 100644 index c097c36e9ac..00000000000 --- a/src/sage/modular/modform/test.py +++ /dev/null @@ -1,125 +0,0 @@ -r""" -Run difficult calculations that test the modular forms -functionality. - -There is currently no good system for timing these doctests across -all platforms, so I am turning these all into comments (so that they -are not counted against are doctest coverage), noting that we should -use these when (if?) we one day have a good regression testing -system in place. - -Craig Citro - - -from sage.all import * - -m=0; t=0; tw=0 - -def pre(): - global m, t, tw - m = get_memory_usage() - t = cputime() - tw = walltime() - - -def post(): - global m,t - print("total time: %s (wall: %.2f); memory usage diff: %sMB"%(cputime(t), - walltime(tw), get_memory_usage() - m)) - -def test1(): - pre() - for N in range(1,75): - M = ModularForms(N,2) - print(M) - print(M.basis()) - post() - -def test2(): - pre() - for N in range(1,30): - M = ModularForms(Gamma1(N),2) - print(M) - print(M.basis()) - post() - -def test3(): - pre() - for k in range(2,100): - M = ModularForms(1,k) - print(M) - print(M.basis()) - post() - -def test4(): - pre() - for N in range(1,30): - M = ModularForms(N,4, prec=20) - print(M) - print(M.basis()) - post() - -def test5(): - pre() - for N in range(1,50): - M = ModularForms(N,2, prec=30) - print(M) - print(M.basis()) - post() - -def test6(): - pre() - for N in range(225,230): - M = ModularForms(N,2,prec=40) - print(M) - print(M.basis()) - post() - -def test7(): - pre() - for k in range(2,30): - M = ModularForms(2,k) - print(M) - print(M.basis()) - post() - -def test8(): - pre() - for k in range(2,20): - M = ModularForms(Gamma1(3),k) - print(M) - print(M.basis()) - post() - -def test9(): - pre() - for k in range(2,11): - M = ModularForms(Gamma1(8),k) - M.set_precision(M.dimension()+2) - print(M) - print(M.basis()) - post() - -def test10(): - pre() - for k in range(2,11): - M = ModularForms(k,k) - M.set_precision(M.dimension()+2) - print(M) - print(M.basis()) - post() - -def test11(): - pre() - for i in range(100): - M = ModularForms(randint(1,100),randint(2,6)) - print(M) - print(M.basis()) - post() - -def test12(): - S = CuspForms(23,2) - print(S) - print(S.hecke_operator(2)) - print(S.hecke_operator(2).matrix()) -""" From 3ba8c868ea0068b85a126e5addc0560664274637 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 8 Oct 2021 08:40:29 -0400 Subject: [PATCH 023/122] Trac #32656: drop memory usage information from some ring tests. Some of the tests in sage/rings/tests.py report their memory usage alongside the number of tests that have passed. In preparation for the removal of get_memory_usage(), we drop the extra information from the expected (and actual) output. --- src/sage/rings/tests.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/tests.py b/src/sage/rings/tests.py index 4fd37826771..a3ff284f677 100644 --- a/src/sage/rings/tests.py +++ b/src/sage/rings/tests.py @@ -11,7 +11,6 @@ import sage.misc.prandom as random -from sage.misc.all import get_memory_usage from sage.misc.random_testing import random_testing @@ -324,11 +323,11 @@ def test_random_elements(level=MAX_LEVEL, trials=1): sage: import sage.rings.tests sage: sage.rings.tests.test_random_elements(trials=2, seed=0) - survived 0 tests (memory usage = ...) + survived 0 tests Rational Field -1/2 ---- - survived 1 tests (memory usage = ...) + survived 1 tests Number Field in a with defining polynomial x^2 - 61891 with a = 248.7790184079036? -12 ---- @@ -341,8 +340,7 @@ def test_random_elements(level=MAX_LEVEL, trials=1): r = random_rings(level) i = 0 for R in r: - print("survived %s tests (memory usage = %s)" % - (i, get_memory_usage())) + print("survived %s tests" % i) i += 1 print(R) print(R.random_element()) @@ -373,11 +371,11 @@ def test_random_arith(level=MAX_LEVEL, trials=1): sage: import sage.rings.tests sage: sage.rings.tests.test_random_arith(trials=2, seed=0) - survived 0 tests (memory usage = ...) + survived 0 tests Rational Field -1/2 -1/95 49/95 - survived 1 tests (memory usage = ...) + survived 1 tests Number Field in a with defining polynomial x^2 - 15083 with a = 122.81286577553673? a -2*a - 1 2*a - 30164 @@ -389,8 +387,7 @@ def test_random_arith(level=MAX_LEVEL, trials=1): """ i = 0 for x in random_rings(level): - print("survived %s tests (memory usage = %s)" % - (i, get_memory_usage())) + print("survived %s tests" % i) i += 1 print(x) a = x.random_element() From 5aeba4461bbff370eefe9811673850f458e6d932 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 8 Oct 2021 11:10:58 -0400 Subject: [PATCH 024/122] Trac #32656: avoid using psutil in one FriCAS doctest. We will soon drop psutil as a dependency, so we modify the one FriCAS doctest that uses it to avoid doing so. The goal of the previous test was to ensure that a new FriCAS process (with no children) had been started; we now accomplish that by comparing PIDs. --- src/sage/interfaces/fricas.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 9d1b817d2ba..f04d60dd7e6 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -369,13 +369,15 @@ def _quit_string(self): sage: a.is_running() # optional - fricas False - TESTS:: + TESTS: + + Ensure that a new process is started after ``quit()``:: + + sage: p = fricas.pid() # optional - fricas + sage: fricas.quit() # optional - fricas + sage: fricas.pid() == p # optional - fricas + False - sage: import psutil # optional - fricas - sage: p = fricas.pid(); pr = psutil.Process(p); pr # optional - fricas - - sage: pr.children() # optional - fricas - [] """ return ')quit' From 24319d275b1a54a0cb82fde2b9add66dfac0c173 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 8 Oct 2021 08:42:21 -0400 Subject: [PATCH 025/122] Trac #32656: don't import sage.misc.getusage into sage.misc.all. The sage.misc.getusage module will be removed soon, so we drop this import now. --- src/sage/misc/all.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 375b8e852f1..c43f18eefed 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -71,8 +71,6 @@ from .reset import reset, restore -from .getusage import get_memory_usage - from .mathml import mathml from .defaults import (set_default_variable_name, From cbeaedf2639bb1af3f0e7823b6a027bcc56ea8d3 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:50:20 -0400 Subject: [PATCH 026/122] Trac #32656: remove two doctests for memory leaks. Two modules, sage.geometry.polyhedron.base and sage.graphs.bipartite_graph, contain doctests that call get_memory_usage() before and after some operation to detect memory leaks within the operation. Regardless of their merit, we intend to eliminate the psutil package and the get_memory_usage() function with it. In preparation for that, we must remove these two doctests. --- src/sage/geometry/polyhedron/base.py | 12 ------------ src/sage/graphs/bipartite_graph.py | 20 -------------------- 2 files changed, 32 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 47063568802..828b0289262 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6635,18 +6635,6 @@ def face_lattice(self): sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0)], vertices=[(0,0)]).face_lattice().level_sets()] [[()], [(0, 1)]] - Test that computing the face lattice does not lead to a memory leak:: - - sage: import gc - sage: _ = gc.collect() - sage: P = polytopes.cube() - sage: a = P.face_lattice() - sage: n = get_memory_usage() - sage: P = polytopes.cube() - sage: a = P.face_lattice() - sage: _ = gc.collect() - sage: n == get_memory_usage() - True """ from sage.combinat.posets.lattices import FiniteLatticePoset return FiniteLatticePoset(self.hasse_diagram()) diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 415f1fa34d9..07ce4ef8b04 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -310,26 +310,6 @@ def __init__(self, data=None, partition=None, check=True, *args, **kwds): sage: partition = [list(range(5)), list(range(5, 10))] sage: B = BipartiteGraph(P, partition, check=False) - TESTS: - - Test that the memory leak in :trac:`31313` is fixed:: - - sage: A = Matrix(ZZ, 100, 125) - sage: for i in range(A.nrows()): - ....: for j in Subsets(A.ncols()).random_element(): - ....: A[i, j - 1] = 1 - sage: def make_bip_graph(A): - ....: G = BipartiteGraph(A) - sage: for _ in range(10): - ....: make_bip_graph(A) - sage: import gc - sage: _ = gc.collect() - sage: start_mem = get_memory_usage() - sage: for _ in range(10): - ....: make_bip_graph(A) - sage: _ = gc.collect() - sage: print(round(get_memory_usage() - start_mem)) - 0.0 """ if kwds is None: kwds = {'loops': False} From 1ea7b697e812302dfca03a1c7fe41c9a8dc3c98e Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:48:22 -0400 Subject: [PATCH 027/122] Trac #32656: eliminate manual GAP memory management. Our GAP interface provides a few functions to get/set the GAP memory pool size. That size used when initializing the GAP interface, being passed as the argument to GAP's "-o" and "-s" command-line flags: https://www.gap-system.org/Manuals/doc/ref/chap3.html In preparation for the removal of psutil and sage.misc.getusage, we remove all of this custom memory management code. The "-o" flag specifies an upper bound, and can simply be omitted. We also omit the "-s" flag in this commit, but that flag is a bit more delicate, since it may affect users with custom GAP modules. However, if the omission of "-s" causes a problem down the line, and if the GAP documentation's assumptions about mmap et al. are correct, then we can simply add back the "-s" flag with a large, static value. --- src/sage/all.py | 8 --- src/sage/interfaces/all.py | 2 +- src/sage/interfaces/gap.py | 93 ------------------------------ src/sage/libs/gap/util.pyx | 30 ++++------ src/sage_docbuild/build_options.py | 4 -- 5 files changed, 12 insertions(+), 125 deletions(-) diff --git a/src/sage/all.py b/src/sage/all.py index 4fa89329e81..6a4035af16a 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -75,14 +75,6 @@ if deprecationWarning in warnings.filters: warnings.filters.remove(deprecationWarning) -# The psutil swap_memory() function tries to collect some statistics -# that may not be available and that we don't need. Hide the warnings -# that are emitted if the stats aren't available (Trac #28329). That -# function is called in two places, so let's install this filter -# before the first one is imported from sage.misc.all below. -warnings.filterwarnings('ignore', category=RuntimeWarning, - message=r"'sin' and 'sout' swap memory stats couldn't be determined") - # Ignore all deprecations from IPython etc. warnings.filterwarnings('ignore', category=DeprecationWarning, module='(IPython|ipykernel|jupyter_client|jupyter_core|nbformat|notebook|ipywidgets|storemagic|jedi)') diff --git a/src/sage/interfaces/all.py b/src/sage/interfaces/all.py index 5bb00ff70b4..0a71ed81ae7 100644 --- a/src/sage/interfaces/all.py +++ b/src/sage/interfaces/all.py @@ -7,7 +7,7 @@ from .axiom import Axiom, axiom from .fricas import FriCAS, fricas -from .gap import gap, gap_reset_workspace, set_gap_memory_pool_size, Gap +from .gap import gap, gap_reset_workspace, Gap from .gap3 import gap3, gap3_version, Gap3 lazy_import('sage.interfaces.genus2reduction', ['genus2reduction', 'Genus2reduction']) from .gfan import gfan, Gfan diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 3185fcccc22..5ee4c9edaaf 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -214,95 +214,6 @@ def gap_command(use_workspace_cache=True, local=True): else: return gap_cmd, True -############ Set the GAP memory pool size - -# you should always use get_gap_memory_pool_size() to access this value -gap_memory_pool_size = None - -def set_gap_memory_pool_size(size_in_bytes): - """ - Set the desired gap memory pool size. - - Subsequently started GAP instances will use this as default. - Already running instances are unchanged. - - GAP will only reserve ``size_in_bytes`` address space. Unless you - actually start a big GAP computation, the memory will not be - used. However, corresponding swap space will be reserved so that - GAP will always be able to use the reserved address space if - needed. While nothing is actually written to disc as long as you - don't run a big GAP computation, the reserved swap space will not - be available for other processes. - - INPUT: - - - ``size_in_bytes`` -- integer. The desired memory pool size. - - EXAMPLES:: - - sage: from sage.interfaces.gap import \ - ....: get_gap_memory_pool_size, set_gap_memory_pool_size - sage: n = get_gap_memory_pool_size() - sage: set_gap_memory_pool_size(n) - sage: n == get_gap_memory_pool_size() - True - sage: n # random output - 1534059315 - """ - global gap_memory_pool_size - gap_memory_pool_size = size_in_bytes - -def get_gap_memory_pool_size(): - """ - Get the gap memory pool size for new GAP processes. - - EXAMPLES:: - - sage: from sage.interfaces.gap import get_gap_memory_pool_size - sage: get_gap_memory_pool_size() # random output - 1534059315 - """ - global gap_memory_pool_size - if gap_memory_pool_size is not None: - return gap_memory_pool_size - - import psutil - from sage.misc.getusage import virtual_memory_limit - mem = psutil.virtual_memory() - swap = psutil.swap_memory() - vmax = virtual_memory_limit() - - suggested_size = max(swap.free // 10, mem.available // 50) - # Don't eat all address space if the user set ulimit -v - suggested_size = min(suggested_size, vmax // 10) - # ~220MB is the minimum for long doctests - suggested_size = max(suggested_size, 400 * 1024**2) - return suggested_size - - -def _get_gap_memory_pool_size_MB(): - """ - Return the gap memory pool size suitable for usage on the GAP - command line. - - The GAP 4.5.6 command line parser had issues with large numbers, so - we return it in megabytes. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.interfaces.gap import \ - ....: _get_gap_memory_pool_size_MB - sage: _get_gap_memory_pool_size_MB() # random output - '1467m' - """ - pool = get_gap_memory_pool_size() - pool = (pool // (1024**2)) + 1 - return str(pool)+'m' - ############ Classes with methods for both the GAP3 and GAP4 interface @@ -1145,10 +1056,6 @@ def __init__(self, max_workspace_size=None, # -T: disable interactive break loop when encountering errors # -E: disable readline support cmd += " -b -p -T -E" - if max_workspace_size is None: - max_workspace_size = _get_gap_memory_pool_size_MB() - cmd += ' -o ' + str(max_workspace_size) - cmd += ' -s ' + str(max_workspace_size) cmd += ' -m 64m ' # attempt at a workaround for http://tracker.gap-system.org/issues/224 cmd += ' ' + os.path.join(SAGE_EXTCODE, 'gap', 'sage.g') Expect.__init__(self, diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index f98a71328b3..c501a26140e 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -238,31 +238,23 @@ cdef initialize(): dlclose(handle) # Define argv variable, which we will pass in to - # initialize GAP. Note that we must pass define the memory pool - # size! + # initialize GAP. cdef char* argv[18] argv[0] = "sage" argv[1] = "-l" s = str_to_bytes(gap_root(), FS_ENCODING, "surrogateescape") argv[2] = s - from sage.interfaces.gap import _get_gap_memory_pool_size_MB - memory_pool = str_to_bytes(_get_gap_memory_pool_size_MB()) - argv[3] = "-o" - argv[4] = memory_pool - argv[5] = "-s" - argv[6] = memory_pool - - argv[7] = "-m" - argv[8] = "64m" - - argv[9] = "-q" # no prompt! - argv[10] = "-E" # don't use readline as this will interfere with Python - argv[11] = "--nointeract" # Implies -T - argv[12] = "-x" # set the "screen" width so that GAP is less likely to - argv[13] = "4096" # insert newlines when printing objects - # 4096 unfortunately is the hard-coded max, but should - # be long enough for most cases + argv[3] = "-m" + argv[4] = "64m" + + argv[5] = "-q" # no prompt! + argv[6] = "-E" # don't use readline as this will interfere with Python + argv[7] = "--nointeract" # Implies -T + argv[8] = "-x" # set the "screen" width so that GAP is less likely to + argv[9] = "4096" # insert newlines when printing objects + # 4096 unfortunately is the hard-coded max, but should + # be long enough for most cases cdef int argc = 14 # argv[argc] must be NULL diff --git a/src/sage_docbuild/build_options.py b/src/sage_docbuild/build_options.py index ddc88a39f0e..44bb883c021 100644 --- a/src/sage_docbuild/build_options.py +++ b/src/sage_docbuild/build_options.py @@ -23,10 +23,6 @@ # Number of threads to use for parallel-building the documentation. NUM_THREADS = int(os.environ.get('SAGE_NUM_THREADS', 1)) -# Minimize GAP RAM usage in the builder, docbuild already uses too much -from sage.interfaces.gap import set_gap_memory_pool_size -set_gap_memory_pool_size(80 * 1024 * 1024) - INCREMENTAL_BUILD = os.path.isdir(SAGE_DOC) # Error out on errors From 082ed59394834a1052d56a5189dfdee2062c652c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 21:06:06 -0400 Subject: [PATCH 028/122] Trac #32656: use static stack size bound for PARI. Cypari requires that we pass both an initial stack size and a maximum stack size to its Pari() constructor. Previously, the initial stack size was chosen to be 1MB, and the maximum was set at 25% of whatever the virtual_memory_limit() function returns. In preparation for the removal of virtual_memory_limit(), we change that upper limit to a static value of 1GiB. Since this is only a bound, we should be able to increase it in the future if 1GiB proves too low; I quote the docstring for Pari.__init__ from cypari2, When the PARI system is already initialized, the PARI stack is only grown if ``size`` is greater than the current stack... --- src/sage/libs/pari/__init__.py | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/sage/libs/pari/__init__.py b/src/sage/libs/pari/__init__.py index 507b98c5098..bc20d9bdf3d 100644 --- a/src/sage/libs/pari/__init__.py +++ b/src/sage/libs/pari/__init__.py @@ -173,30 +173,10 @@ """ def _get_pari_instance(): - # There are two constraints for the virtual stack size: - # 1) on 32-bit systems, even virtual memory can be a scarce - # resource since it is limited by 4GB (of which the kernel - # needs a significant part) - # 2) the system should actually be able to handle a stack size - # as large as the complete virtual stack. - # As a simple heuristic, we set the virtual stack to 1/4 of the - # virtual memory. - from sage.misc.getusage import virtual_memory_limit - - sizemax = virtual_memory_limit() // 4 - - from sage.env import CYGWIN_VERSION - if CYGWIN_VERSION and CYGWIN_VERSION < (2, 5, 2): - # Cygwin's mmap is broken for large NORESERVE mmaps (>~ 4GB) See - # http://trac.sagemath.org/ticket/20463 So we set the max stack - # size to a little below 4GB (putting it right on the margin proves - # too fragile) - # - # The underlying issue is fixed in Cygwin v2.5.2 - sizemax = min(sizemax, 0xf0000000) - from cypari2 import Pari - P = Pari(1000000, sizemax) + stack_initial = 1024*1024 + stack_max = 1024*stack_initial + P = Pari(stack_initial, stack_max) # pari_init_opts() overrides MPIR's memory allocation functions, # so we need to reset them. From b2667b513fe8ad2671e2b9ffabbfd6d176a3d1a4 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 21:06:31 -0400 Subject: [PATCH 029/122] Trac #32656: remove the sage.misc.getusage module. There are no more consumers of this module, and we plan to eliminate the psutil package that it is based upon soon, so here we remove the module itself and drop it from the reference manual. --- src/doc/en/reference/misc/index.rst | 1 - src/sage/misc/getusage.py | 79 ----------------------------- 2 files changed, 80 deletions(-) delete mode 100644 src/sage/misc/getusage.py diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index dbaa7602438..489fc699447 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -281,7 +281,6 @@ Miscellaneous Inspection and Development Tools sage/docs/instancedoc sage/misc/sageinspect sage/misc/edit_module - sage/misc/getusage sage/misc/classgraph sage/misc/dev_tools sage/misc/function_mangling diff --git a/src/sage/misc/getusage.py b/src/sage/misc/getusage.py deleted file mode 100644 index cf9632dcd54..00000000000 --- a/src/sage/misc/getusage.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Get resource usage of process - -AUTHORS: - -- William Stein (2006-03-04): initial version - -- Jeroen Demeyer (2016-11-14): implement as thin wrapper over - ``psutil`` package -""" - -# **************************************************************************** -# Copyright (C) 2006 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** - - -import sys - - -def get_memory_usage(t=None): - """ - Return the memory usage of the current process in megabytes. - - INPUT: - - - ``t`` -- a float (default: None); output of an earlier call. - If this is given, return the current usage minus `t`. - - OUTPUT: a float representing the number of megabytes used. - - EXAMPLES:: - - sage: t = get_memory_usage(); t # random - 873.98046875 - sage: type(t) - <... 'float'> - """ - import psutil - m = psutil.Process().memory_info().vms / 1048576 - if t is None: - return m - else: - return m - t - - -def virtual_memory_limit(): - """ - Return the upper limit for virtual memory usage. - - This is the value set by ``ulimit -v`` at the command line or a - practical limit if no limit is set. In any case, the value is - bounded by ``sys.maxsize``. - - OUTPUT: - - Integer. The virtual memory limit in bytes. - - EXAMPLES:: - - sage: from sage.misc.getusage import virtual_memory_limit - sage: virtual_memory_limit() > 0 - True - sage: virtual_memory_limit() <= sys.maxsize - True - """ - import resource - try: - vmax = resource.getrlimit(resource.RLIMIT_AS)[0] - except resource.error: - vmax = resource.RLIM_INFINITY - if vmax == resource.RLIM_INFINITY: - import psutil - vmax = psutil.virtual_memory().total + psutil.swap_memory().total - return min(vmax, sys.maxsize) From 7c8de66769d155b0e4c58253a94a22a053c359db Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 6 Oct 2021 20:30:45 -0400 Subject: [PATCH 030/122] Trac #32656: remove the psutil package. With all consumers of the psutil package gone, we delete the SPKG itself, and remove all traces of it from the build system. --- Pipfile.m4 | 1 - build/make/Makefile.in | 3 +- build/pkgs/psutil/SPKG.rst | 20 - build/pkgs/psutil/checksums.ini | 4 - build/pkgs/psutil/dependencies | 1 - build/pkgs/psutil/distros/conda.txt | 1 - build/pkgs/psutil/distros/macports.txt | 1 - build/pkgs/psutil/distros/opensuse.txt | 1 - build/pkgs/psutil/distros/repology.txt | 2 - build/pkgs/psutil/install-requires.txt | 1 - build/pkgs/psutil/package-version.txt | 1 - .../pkgs/psutil/patches/cygwin-support.patch | 4644 ----------------- build/pkgs/psutil/spkg-install.in | 7 - build/pkgs/psutil/type | 1 - src/requirements.txt.m4 | 1 - src/setup.cfg.m4 | 1 - 16 files changed, 1 insertion(+), 4689 deletions(-) delete mode 100644 build/pkgs/psutil/SPKG.rst delete mode 100644 build/pkgs/psutil/checksums.ini delete mode 100644 build/pkgs/psutil/dependencies delete mode 100644 build/pkgs/psutil/distros/conda.txt delete mode 100644 build/pkgs/psutil/distros/macports.txt delete mode 100644 build/pkgs/psutil/distros/opensuse.txt delete mode 100644 build/pkgs/psutil/distros/repology.txt delete mode 100644 build/pkgs/psutil/install-requires.txt delete mode 100644 build/pkgs/psutil/package-version.txt delete mode 100644 build/pkgs/psutil/patches/cygwin-support.patch delete mode 100644 build/pkgs/psutil/spkg-install.in delete mode 100644 build/pkgs/psutil/type diff --git a/Pipfile.m4 b/Pipfile.m4 index 76df035b999..e7dd86744a0 100644 --- a/Pipfile.m4 +++ b/Pipfile.m4 @@ -24,7 +24,6 @@ numpy = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../numpy/package-version.txt)')" cysignals = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../cysignals/package-version.txt)')" cypari2 = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../cypari/package-version.txt)')" gmpy2 = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../gmpy2/package-version.txt)')" -psutil = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../psutil/package-version.txt)')" pexpect = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../pexpect/package-version.txt)')" ipython = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../ipython/package-version.txt)')" sympy = "==esyscmd(`printf $(sed "s/[.]p.*//;" ../sympy/package-version.txt)')" diff --git a/build/make/Makefile.in b/build/make/Makefile.in index bd8ca2a99da..acdfd997537 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -288,8 +288,7 @@ python3-SAGE_VENV-no-deps: setuptools-clean # Everything needed to start up Sage using "./sage". Of course, not # every part of Sage will work. It does not include Maxima for example. -SAGERUNTIME = sagelib $(inst_ipython) $(inst_pexpect) \ - $(inst_psutil) +SAGERUNTIME = sagelib $(inst_ipython) $(inst_pexpect) all-sageruntime: toolchain-deps +$(MAKE_REC) $(SAGERUNTIME) diff --git a/build/pkgs/psutil/SPKG.rst b/build/pkgs/psutil/SPKG.rst deleted file mode 100644 index e6ddbcc038b..00000000000 --- a/build/pkgs/psutil/SPKG.rst +++ /dev/null @@ -1,20 +0,0 @@ -psutil: Python library to retrieve information on processes and system utilization -================================================================================== - -Description ------------ - -psutil is a cross-platform library for retrieving information on running -processes and system utilization (CPU, memory, disks, network) in -Python. - -License -------- - -3-clause BSD license - - -Upstream Contact ----------------- - -https://github.com/giampaolo/psutil/ diff --git a/build/pkgs/psutil/checksums.ini b/build/pkgs/psutil/checksums.ini deleted file mode 100644 index 8154abe0921..00000000000 --- a/build/pkgs/psutil/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=psutil-VERSION.tar.gz -sha1=94bfd957caf439c504858dfbfea4c43581698d9c -md5=c9aa2599dcd9e5b59d71b6660d396062 -cksum=1440797320 diff --git a/build/pkgs/psutil/dependencies b/build/pkgs/psutil/dependencies deleted file mode 100644 index da2b0925acd..00000000000 --- a/build/pkgs/psutil/dependencies +++ /dev/null @@ -1 +0,0 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) diff --git a/build/pkgs/psutil/distros/conda.txt b/build/pkgs/psutil/distros/conda.txt deleted file mode 100644 index a4d92cc08db..00000000000 --- a/build/pkgs/psutil/distros/conda.txt +++ /dev/null @@ -1 +0,0 @@ -psutil diff --git a/build/pkgs/psutil/distros/macports.txt b/build/pkgs/psutil/distros/macports.txt deleted file mode 100644 index 5b16d848a6d..00000000000 --- a/build/pkgs/psutil/distros/macports.txt +++ /dev/null @@ -1 +0,0 @@ -py-psutil diff --git a/build/pkgs/psutil/distros/opensuse.txt b/build/pkgs/psutil/distros/opensuse.txt deleted file mode 100644 index ac842e4b343..00000000000 --- a/build/pkgs/psutil/distros/opensuse.txt +++ /dev/null @@ -1 +0,0 @@ -python3-psutil diff --git a/build/pkgs/psutil/distros/repology.txt b/build/pkgs/psutil/distros/repology.txt deleted file mode 100644 index 651f0493a90..00000000000 --- a/build/pkgs/psutil/distros/repology.txt +++ /dev/null @@ -1,2 +0,0 @@ -psutil -python:psutil diff --git a/build/pkgs/psutil/install-requires.txt b/build/pkgs/psutil/install-requires.txt deleted file mode 100644 index cafe08598f8..00000000000 --- a/build/pkgs/psutil/install-requires.txt +++ /dev/null @@ -1 +0,0 @@ -psutil >=5.2.0 diff --git a/build/pkgs/psutil/package-version.txt b/build/pkgs/psutil/package-version.txt deleted file mode 100644 index 3d61945d1ef..00000000000 --- a/build/pkgs/psutil/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -5.2.0.p2 diff --git a/build/pkgs/psutil/patches/cygwin-support.patch b/build/pkgs/psutil/patches/cygwin-support.patch deleted file mode 100644 index 96538e65d99..00000000000 --- a/build/pkgs/psutil/patches/cygwin-support.patch +++ /dev/null @@ -1,4644 +0,0 @@ -diff --git a/psutil/__init__.py b/psutil/__init__.py -index 6b88776..cb0f840 100644 ---- a/psutil/__init__.py -+++ b/psutil/__init__.py -@@ -71,6 +71,7 @@ from ._common import OSX - from ._common import POSIX # NOQA - from ._common import SUNOS - from ._common import WINDOWS -+from ._common import CYGWIN - - if LINUX: - # This is public API and it will be retrieved from _pslinux.py -@@ -147,6 +148,10 @@ elif SUNOS: - # _pssunos.py via sys.modules. - PROCFS_PATH = "/proc" - -+elif CYGWIN: -+ PROCFS_PATH = "/proc" -+ from . import _pscygwin as _psplatform -+ - else: # pragma: no cover - raise NotImplementedError('platform %s is not supported' % sys.platform) - -@@ -174,7 +179,7 @@ __all__ = [ - "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED", - - "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS", -- "WINDOWS", -+ "WINDOWS", "CYGWIN", - - # classes - "Process", "Popen", -@@ -326,6 +331,13 @@ _psplatform.ZombieProcess = ZombieProcess - _psplatform.AccessDenied = AccessDenied - _psplatform.TimeoutExpired = TimeoutExpired - -+# TODO: probably having these both is superfulous, and platform -+# specific modules can import these exceptions from _common -+_common.NoSuchProcess = NoSuchProcess -+_common.ZombieProcess = ZombieProcess -+_common.AccessDenied = AccessDenied -+_common.TimeoutExpired = TimeoutExpired -+ - - # ===================================================================== - # --- Process class -@@ -942,7 +954,7 @@ class Process(object): - # (self) it means child's PID has been reused - if self.create_time() <= p.create_time(): - ret.append(p) -- except (NoSuchProcess, ZombieProcess): -+ except (AccessDenied, NoSuchProcess, ZombieProcess): - pass - else: # pragma: no cover - # Windows only (faster) -@@ -964,7 +976,7 @@ class Process(object): - for p in process_iter(): - try: - table[p.ppid()].append(p) -- except (NoSuchProcess, ZombieProcess): -+ except (AccessDenied, NoSuchProcess, ZombieProcess): - pass - else: # pragma: no cover - for pid, ppid in ppid_map.items(): -diff --git a/psutil/_common.py b/psutil/_common.py -index 2497226..7b48ddf 100644 ---- a/psutil/_common.py -+++ b/psutil/_common.py -@@ -32,10 +32,14 @@ if sys.version_info >= (3, 4): - else: - enum = None - -+ -+PY3 = sys.version_info[0] == 3 -+ -+ - __all__ = [ - # OS constants - 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'OSX', 'POSIX', 'SUNOS', -- 'WINDOWS', -+ 'WINDOWS', 'CYGWIN', - # connection constants - 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', - 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', -@@ -72,6 +76,7 @@ OPENBSD = sys.platform.startswith("openbsd") - NETBSD = sys.platform.startswith("netbsd") - BSD = FREEBSD or OPENBSD or NETBSD - SUNOS = sys.platform.startswith("sunos") or sys.platform.startswith("solaris") -+CYGWIN = sys.platform.startswith("cygwin") - - - # =================================================================== -@@ -232,6 +237,18 @@ if AF_UNIX is not None: - del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM - - -+# ===================================================================== -+# -- exceptions -+# ===================================================================== -+ -+ -+# these get overwritten on "import psutil" from the __init__.py file -+NoSuchProcess = None -+ZombieProcess = None -+AccessDenied = None -+TimeoutExpired = None -+ -+ - # =================================================================== - # --- utils - # =================================================================== -@@ -334,6 +351,10 @@ def memoize_when_activated(fun): - return wrapper - - -+def get_procfs_path(): -+ return sys.modules['psutil'].PROCFS_PATH -+ -+ - def isfile_strict(path): - """Same as os.path.isfile() but does not swallow EACCES / EPERM - exceptions, see: -@@ -349,6 +370,55 @@ def isfile_strict(path): - return stat.S_ISREG(st.st_mode) - - -+def open_binary(fname, **kwargs): -+ return open(fname, "rb", **kwargs) -+ -+ -+_FS_ENCODING = sys.getfilesystemencoding() -+_ENCODING_ERRORS_HANDLER = 'surrogateescape' -+ -+ -+# TODO: Move some of these functions to ._compat since they're more for py2/3 -+# compatibility than anything else -+ -+def open_text(fname, **kwargs): -+ """On Python 3 opens a file in text mode by using fs encoding and -+ a proper en/decoding errors handler. -+ On Python 2 this is just an alias for open(name, 'rt'). -+ """ -+ if PY3: -+ # See: -+ # https://github.com/giampaolo/psutil/issues/675 -+ # https://github.com/giampaolo/psutil/pull/733 -+ kwargs.setdefault('encoding', _FS_ENCODING) -+ kwargs.setdefault('errors', _ENCODING_ERRORS_HANDLER) -+ return open(fname, "rt", **kwargs) -+ -+ -+if PY3: -+ def decode(s): -+ return s.decode(encoding=_FS_ENCODING, errors=_ENCODING_ERRORS_HANDLER) -+else: -+ def decode(s): -+ return s -+ -+ -+if PY3: -+ def encode(s, encoding=_FS_ENCODING): -+ return s -+else: -+ def encode(s, encoding=_FS_ENCODING): -+ if isinstance(s, str): -+ return s -+ -+ try: -+ return s.encode(encoding) -+ except UnicodeEncodeError: -+ # Filesystem codec failed, return the plain unicode -+ # string (this should never happen). -+ return s -+ -+ - def path_exists_strict(path): - """Same as os.path.exists() but does not swallow EACCES / EPERM - exceptions, see: -@@ -447,3 +517,23 @@ def deprecated_method(replacement): - return getattr(self, replacement)(*args, **kwargs) - return inner - return outer -+ -+ -+def wrap_exceptions(fun): -+ """Decorator which translates bare OSError and IOError exceptions -+ into NoSuchProcess and AccessDenied. -+ """ -+ @functools.wraps(fun) -+ def wrapper(self, *args, **kwargs): -+ try: -+ return fun(self, *args, **kwargs) -+ except EnvironmentError as err: -+ # ENOENT (no such file or directory) gets raised on open(). -+ # ESRCH (no such process) can get raised on read() if -+ # process is gone in meantime. -+ if err.errno in (errno.ENOENT, errno.ESRCH): -+ raise NoSuchProcess(self.pid, self._name) -+ if err.errno in (errno.EPERM, errno.EACCES): -+ raise AccessDenied(self.pid, self._name) -+ raise -+ return wrapper -diff --git a/psutil/_pscygwin.py b/psutil/_pscygwin.py -new file mode 100644 -index 0000000..ffba639 ---- /dev/null -+++ b/psutil/_pscygwin.py -@@ -0,0 +1,891 @@ -+"""Cygwin platform implementation.""" -+ -+# TODO: Large chunks of this module are copy/pasted from the Linux module; -+# seek out further opportunities for refactoring -+ -+from __future__ import division -+ -+import errno -+import os -+import re -+import sys -+from collections import namedtuple -+ -+from . import _common -+from . import _psposix -+from . import _psutil_cygwin as cext -+from . import _psutil_posix as cext_posix -+from ._common import conn_tmap -+from ._common import decode -+from ._common import encode -+from ._common import get_procfs_path -+from ._common import isfile_strict -+from ._common import memoize_when_activated -+from ._common import open_binary -+from ._common import open_text -+from ._common import popenfile -+from ._common import sockfam_to_enum -+from ._common import socktype_to_enum -+from ._common import usage_percent -+from ._common import wrap_exceptions -+from ._compat import PY3 -+from ._compat import b -+from ._compat import lru_cache -+from ._compat import xrange -+ -+if sys.version_info >= (3, 4): -+ import enum -+else: -+ enum = None -+ -+__extra__all__ = ["PROCFS_PATH"] -+ -+ -+# ===================================================================== -+# --- constants -+# ===================================================================== -+ -+ -+if enum is None: -+ AF_LINK = -1 -+else: -+ AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1}) -+ AF_LINK = AddressFamily.AF_LINK -+ -+ -+# Number of clock ticks per second -+CLOCK_TICKS = os.sysconf("SC_CLK_TCK") -+ -+ -+# TODO: Update me to properly reflect Cygwin-recognized process statuses -+# taken from /fs/proc/array.c -+PROC_STATUSES = { -+ "R": _common.STATUS_RUNNING, -+ "S": _common.STATUS_SLEEPING, -+ "D": _common.STATUS_DISK_SLEEP, -+ "T": _common.STATUS_STOPPED, -+ "t": _common.STATUS_TRACING_STOP, -+ "Z": _common.STATUS_ZOMBIE, -+ "X": _common.STATUS_DEAD, -+ "x": _common.STATUS_DEAD, -+ "K": _common.STATUS_WAKE_KILL, -+ "W": _common.STATUS_WAKING -+} -+ -+CONN_DELETE_TCB = "DELETE_TCB" -+ -+TCP_STATUSES = { -+ cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED, -+ cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT, -+ cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV, -+ cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1, -+ cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2, -+ cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT, -+ cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE, -+ cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, -+ cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK, -+ cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN, -+ cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING, -+ cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB, -+ cext.PSUTIL_CONN_NONE: _common.CONN_NONE, -+} -+ -+ -+ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES, -+ cext.ERROR_ACCESS_DENIED]) -+ -+# ===================================================================== -+# -- exceptions -+# ===================================================================== -+ -+ -+# these get overwritten on "import psutil" from the __init__.py file -+NoSuchProcess = None -+ZombieProcess = None -+AccessDenied = None -+TimeoutExpired = None -+ -+ -+# ===================================================================== -+# --- utils -+# ===================================================================== -+ -+ -+# TODO: Alternatively use Cygwin's API to get this, maybe? -+def cygpid_to_winpid(pid): -+ """ -+ Converts Cygwin's internal PID (the one exposed by all POSIX interfaces) -+ to the associated Windows PID. -+ """ -+ -+ procfs_path = get_procfs_path() -+ path = '%s/%s/winpid' % (procfs_path, pid) -+ if not os.path.exists(path): -+ raise NoSuchProcess(pid) -+ -+ with open_binary('%s/%s/winpid' % (procfs_path, pid)) as f: -+ return int(f.readline().strip()) -+ -+ -+def winpid_to_cygpid(pid): -+ """ -+ Converts a Windows PID to its associated Cygwin PID. -+ """ -+ -+ # TODO: This is quite ineffecient--Cygwin provides an API for this that we -+ # can use later -+ procfs_path = get_procfs_path() -+ for path in os.listdir(procfs_path): -+ if not path.isdigit(): -+ continue -+ -+ winpid_path = '%s/%s/winpid' % (procfs_path, path) -+ -+ if not os.path.exists(winpid_path): -+ continue -+ -+ with open_binary(winpid_path) as f: -+ winpid = int(f.readline().strip()) -+ if winpid == pid: -+ return int(path) -+ -+ raise NoSuchProcess(pid) -+ -+ -+@lru_cache(maxsize=512) -+def _win32_QueryDosDevice(s): -+ return cext.win32_QueryDosDevice(s) -+ -+ -+# TODO: Copied almost verbatim from the windows module, except don't -+# us os.path.join since that uses posix sep -+def convert_dos_path(s): -+ # convert paths using native DOS format like: -+ # "\Device\HarddiskVolume1\Windows\systemew\file.txt" -+ # into: "C:\Windows\systemew\file.txt" -+ if PY3 and not isinstance(s, str): -+ s = s.decode('utf8') -+ rawdrive = '\\'.join(s.split('\\')[:3]) -+ driveletter = _win32_QueryDosDevice(rawdrive) -+ return '%s\\%s' % (driveletter, s[len(rawdrive):]) -+ -+ -+# ===================================================================== -+# --- named tuples -+# ===================================================================== -+ -+ -+scputimes = namedtuple('scputimes', ['user', 'system', 'idle']) -+pmem = namedtuple('pmem', ['rss', 'vms', 'shared', 'text', 'lib', 'data', -+ 'dirty']) -+pmem = namedtuple( -+ 'pmem', ['rss', 'vms', -+ 'num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool', -+ 'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool', -+ 'pagefile', 'peak_pagefile', 'private']) -+pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) -+svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) -+pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss']) -+pmmap_ext = namedtuple( -+ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) -+ntpinfo = namedtuple( -+ 'ntpinfo', ['num_handles', 'ctx_switches', 'user_time', 'kernel_time', -+ 'create_time', 'num_threads', 'io_rcount', 'io_wcount', -+ 'io_rbytes', 'io_wbytes']) -+ -+ -+# ===================================================================== -+# --- system memory -+# ===================================================================== -+ -+ -+def _get_meminfo(): -+ mems = {} -+ with open_binary('%s/meminfo' % get_procfs_path()) as f: -+ for line in f: -+ fields = line.split() -+ mems[fields[0].rstrip(b':')] = int(fields[1]) * 1024 -+ -+ return mems -+ -+ -+def virtual_memory(): -+ """Report virtual memory stats. -+ This implementation matches "free" and "vmstat -s" cmdline -+ utility values and procps-ng-3.3.12 source was used as a reference -+ (2016-09-18): -+ https://gitlab.com/procps-ng/procps/blob/ -+ 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c -+ For reference, procps-ng-3.3.10 is the version available on Ubuntu -+ 16.04. -+ -+ Note about "available" memory: up until psutil 4.3 it was -+ calculated as "avail = (free + buffers + cached)". Now -+ "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as -+ it's more accurate. -+ That matches "available" column in newer versions of "free". -+ """ -+ mems = _get_meminfo() -+ -+ # /proc doc states that the available fields in /proc/meminfo vary -+ # by architecture and compile options, but these 3 values are also -+ # returned by sysinfo(2); as such we assume they are always there. -+ total = mems[b'MemTotal'] -+ free = mems[b'MemFree'] -+ -+ used = total - free -+ -+ # On Windows we are treating avail and free as the same -+ # TODO: Are they really though? -+ avail = free -+ -+ percent = usage_percent((total - avail), total, _round=1) -+ -+ return svmem(total, avail, percent, used, free) -+ -+ -+def swap_memory(): -+ mems = _get_meminfo() -+ total = mems[b'SwapTotal'] -+ free = mems[b'SwapFree'] -+ used = total - free -+ percent = usage_percent(used, total, _round=1) -+ return _common.sswap(total, used, free, percent, 0, 0) -+ -+ -+# ===================================================================== -+# --- CPUs -+# ===================================================================== -+ -+ -+def cpu_times(): -+ """Return a named tuple representing the following system-wide -+ CPU times: -+ (user, nice, system, idle, iowait, irq, softirq [steal, [guest, -+ [guest_nice]]]) -+ Last 3 fields may not be available on all Linux kernel versions. -+ """ -+ procfs_path = get_procfs_path() -+ with open_binary('%s/stat' % procfs_path) as f: -+ values = f.readline().split() -+ fields = values[1:2] + values[3:len(scputimes._fields) + 2] -+ fields = [float(x) / CLOCK_TICKS for x in fields] -+ return scputimes(*fields) -+ -+ -+def per_cpu_times(): -+ """Return a list of namedtuple representing the CPU times -+ for every CPU available on the system. -+ """ -+ procfs_path = get_procfs_path() -+ cpus = [] -+ with open_binary('%s/stat' % procfs_path) as f: -+ # get rid of the first line which refers to system wide CPU stats -+ f.readline() -+ for line in f: -+ if line.startswith(b'cpu'): -+ values = line.split() -+ fields = values[1:2] + values[3:len(scputimes._fields) + 2] -+ fields = [float(x) / CLOCK_TICKS for x in fields] -+ entry = scputimes(*fields) -+ cpus.append(entry) -+ return cpus -+ -+ -+def cpu_count_logical(): -+ """Return the number of logical CPUs in the system.""" -+ try: -+ return os.sysconf("SC_NPROCESSORS_ONLN") -+ except ValueError: -+ # as a second fallback we try to parse /proc/cpuinfo -+ num = 0 -+ with open_binary('%s/cpuinfo' % get_procfs_path()) as f: -+ for line in f: -+ if line.lower().startswith(b'processor'): -+ num += 1 -+ -+ # unknown format (e.g. amrel/sparc architectures), see: -+ # https://github.com/giampaolo/psutil/issues/200 -+ # try to parse /proc/stat as a last resort -+ if num == 0: -+ search = re.compile('cpu\d') -+ with open_text('%s/stat' % get_procfs_path()) as f: -+ for line in f: -+ line = line.split(' ')[0] -+ if search.match(line): -+ num += 1 -+ -+ if num == 0: -+ # mimic os.cpu_count() -+ return None -+ return num -+ -+ -+def cpu_count_physical(): -+ """Return the number of physical cores in the system.""" -+ mapping = {} -+ current_info = {} -+ with open_binary('%s/cpuinfo' % get_procfs_path()) as f: -+ for line in f: -+ line = line.strip().lower() -+ if not line: -+ # new section -+ if (b'physical id' in current_info and -+ b'cpu cores' in current_info): -+ mapping[current_info[b'physical id']] = \ -+ current_info[b'cpu cores'] -+ current_info = {} -+ else: -+ # ongoing section -+ if (line.startswith(b'physical id') or -+ line.startswith(b'cpu cores')): -+ key, value = line.split(b'\t:', 1) -+ current_info[key] = int(value) -+ -+ # mimic os.cpu_count() -+ return sum(mapping.values()) or None -+ -+ -+# TODO: Works mostly the same as on Linux, but softirq is not available; -+# meanwhile the Windows module supports number of system calls, but this -+# implementation does not. There's also a question of whether we want it -+# to count Cygwin "system" calls, actual Windows system calls, or what... -+# It's a somewhat ill-defined field on Cygwin; may have to come back to it -+# TODO: Depending on what we decide to do about syscalls, this implementation -+# could be shared with the Linux implementation with some minor tweaks to the -+# latter -+def cpu_stats(): -+ with open_binary('%s/stat' % get_procfs_path()) as f: -+ ctx_switches = None -+ interrupts = None -+ for line in f: -+ if line.startswith(b'ctxt'): -+ ctx_switches = int(line.split()[1]) -+ elif line.startswith(b'intr'): -+ interrupts = int(line.split()[1]) -+ if ctx_switches is not None and interrupts is not None: -+ break -+ syscalls = 0 -+ soft_interrupts = 0 -+ return _common.scpustats( -+ ctx_switches, interrupts, soft_interrupts, syscalls) -+ -+ -+# ===================================================================== -+# --- network -+# ===================================================================== -+ -+ -+# TODO: This might merit a little further work to get the "friendly" -+# interface names instead of interface UUIDs -+net_if_addrs = cext_posix.net_if_addrs -+ -+ -+def net_connections(kind, _pid=-1): -+ """Return socket connections. If pid == -1 return system-wide -+ connections (as opposed to connections opened by one process only). -+ """ -+ if kind not in conn_tmap: -+ raise ValueError("invalid %r kind argument; choose between %s" -+ % (kind, ', '.join([repr(x) for x in conn_tmap]))) -+ elif kind == 'unix': -+ raise ValueError("invalid %r kind argument; although UNIX sockets " -+ "are supported on Cygwin it is not possible to " -+ "enumerate the UNIX sockets opened by a process" -+ % kind) -+ -+ families, types = conn_tmap[kind] -+ if _pid > 0: -+ _pid = cygpid_to_winpid(_pid) -+ -+ # Note: This lists *all* net connections on the system, not just ones by -+ # Cygwin processes; below we whittle it down just to Cygwin processes, but -+ # we might consider an extra option for showing non-Cygwin processes as -+ # well -+ rawlist = cext.net_connections(_pid, families, types) -+ ret = set() -+ for item in rawlist: -+ fd, fam, type, laddr, raddr, status, pid = item -+ status = TCP_STATUSES[status] -+ fam = sockfam_to_enum(fam) -+ type = socktype_to_enum(type) -+ if _pid == -1: -+ try: -+ pid = winpid_to_cygpid(pid) -+ except NoSuchProcess: -+ continue -+ nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) -+ else: -+ nt = _common.pconn(fd, fam, type, laddr, raddr, status) -+ ret.add(nt) -+ return list(ret) -+ -+ -+def net_if_stats(): -+ ret = {} -+ if_stats = cext.net_if_stats() -+ # NOTE: Cygwin does some tricky things in how getifaddrs is handled -+ # which our net_if_stats does not do, such that net_if_stats does not -+ # return all the same interfaces as net_if_stats, so here we artifically -+ # limit the net_if_stats() results to just those interfaces returned by -+ # net_if_addrs -+ if_names = set(addr[0] for addr in net_if_addrs()) -+ for name, items in if_stats.items(): -+ if name not in if_names: -+ continue -+ name = encode(name) -+ isup, duplex, speed, mtu = items -+ if hasattr(_common, 'NicDuplex'): -+ duplex = _common.NicDuplex(duplex) -+ ret[name] = _common.snicstats(isup, duplex, speed, mtu) -+ return ret -+ -+ -+def net_io_counters(): -+ ret = cext.net_io_counters() -+ return dict([(encode(k), v) for k, v in ret.items()]) -+ -+ -+# ===================================================================== -+# --- sensors -+# ===================================================================== -+ -+ -+# TODO: Copied verbatim from the Windows module -+def sensors_battery(): -+ """Return battery information.""" -+ # For constants meaning see: -+ # https://msdn.microsoft.com/en-us/library/windows/desktop/ -+ # aa373232(v=vs.85).aspx -+ acline_status, flags, percent, secsleft = cext.sensors_battery() -+ power_plugged = acline_status == 1 -+ no_battery = bool(flags & 128) -+ charging = bool(flags & 8) -+ -+ if no_battery: -+ return None -+ if power_plugged or charging: -+ secsleft = _common.POWER_TIME_UNLIMITED -+ elif secsleft == -1: -+ secsleft = _common.POWER_TIME_UNKNOWN -+ -+ return _common.sbattery(percent, secsleft, power_plugged) -+ -+ -+# ===================================================================== -+# --- disks -+# ===================================================================== -+ -+ -+disk_io_counters = cext.disk_io_counters -+ -+ -+disk_usage = _psposix.disk_usage -+ -+ -+# TODO: Copied verbatim from the Linux module; refactor -+def disk_partitions(all=False): -+ """Return mounted disk partitions as a list of namedtuples""" -+ fstypes = set() -+ with open_text("%s/filesystems" % get_procfs_path()) as f: -+ for line in f: -+ line = line.strip() -+ if not line.startswith("nodev"): -+ fstypes.add(line.strip()) -+ else: -+ # ignore all lines starting with "nodev" except "nodev zfs" -+ fstype = line.split("\t")[1] -+ if fstype == "zfs": -+ fstypes.add("zfs") -+ -+ retlist = [] -+ partitions = cext.disk_partitions() -+ for partition in partitions: -+ device, mountpoint, fstype, opts = partition -+ if device == 'none': -+ device = '' -+ if not all: -+ if device == '' or fstype not in fstypes: -+ continue -+ ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) -+ retlist.append(ntuple) -+ return retlist -+ -+ -+# ===================================================================== -+# --- other system functions -+# ===================================================================== -+ -+ -+def boot_time(): -+ """The system boot time expressed in seconds since the epoch.""" -+ return cext.boot_time() -+ -+ -+# TODO: Copied verbatim from the Linux module -+def users(): -+ """Return currently connected users as a list of namedtuples.""" -+ retlist = [] -+ rawlist = cext.users() -+ for item in rawlist: -+ user, tty, hostname, tstamp, user_process = item -+ # note: the underlying C function includes entries about -+ # system boot, run level and others. We might want -+ # to use them in the future. -+ if not user_process: -+ continue -+ if hostname == ':0.0' or hostname == ':0': -+ hostname = 'localhost' -+ nt = _common.suser(user, tty or None, hostname, tstamp) -+ retlist.append(nt) -+ return retlist -+ -+ -+# ===================================================================== -+# --- processes -+# ===================================================================== -+ -+ -+def pids(): -+ """Returns a list of PIDs currently running on the system.""" -+ return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()] -+ -+ -+def pid_exists(pid): -+ """Check For the existence of a unix pid.""" -+ return _psposix.pid_exists(pid) -+ -+ -+class Process(object): -+ """Cygwin process implementation.""" -+ -+ __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_winpid"] -+ -+ def __init__(self, pid): -+ self.pid = pid -+ self._name = None -+ self._ppid = None -+ self._procfs_path = get_procfs_path() -+ self._winpid = cygpid_to_winpid(pid) -+ -+ @memoize_when_activated -+ def _parse_stat_file(self): -+ """Parse /proc/{pid}/stat file. Return a list of fields where -+ process name is in position 0. -+ Using "man proc" as a reference: where "man proc" refers to -+ position N, always subscract 2 (e.g starttime pos 22 in -+ 'man proc' == pos 20 in the list returned here). -+ """ -+ -+ stat_filename = "%s/%s/stat" % (self._procfs_path, self.pid) -+ -+ # NOTE: On Cygwin, if the stat file exists but reading it raises an -+ # EINVAL, this indicates that we are probably looking at a zombie -+ # process (this doesn't happen in all cases--seems to be a bug in -+ # Cygwin) -+ try: -+ with open_binary(stat_filename) as f: -+ data = f.read() -+ except IOError as err: -+ if (err.errno == errno.EINVAL and -+ os.path.exists(err.filename)): -+ raise ZombieProcess(self.pid, self._name, self._ppid) -+ -+ raise -+ # Process name is between parentheses. It can contain spaces and -+ # other parentheses. This is taken into account by looking for -+ # the first occurrence of "(" and the last occurence of ")". -+ rpar = data.rfind(b')') -+ name = data[data.find(b'(') + 1:rpar] -+ fields_after_name = data[rpar + 2:].split() -+ return [name] + fields_after_name -+ -+ @memoize_when_activated -+ def _read_status_file(self): -+ with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f: -+ return f.read() -+ -+ @memoize_when_activated -+ def oneshot_info(self): -+ """Return multiple information about this process as a -+ raw tuple. -+ """ -+ return cext.proc_info(self._winpid) -+ -+ def oneshot_enter(self): -+ self._parse_stat_file.cache_activate() -+ self._read_status_file.cache_activate() -+ self.oneshot_info.cache_activate() -+ -+ def oneshot_exit(self): -+ self._parse_stat_file.cache_deactivate() -+ self._read_status_file.cache_deactivate() -+ self.oneshot_info.cache_deactivate() -+ -+ @wrap_exceptions -+ def name(self): -+ name = self._parse_stat_file()[0] -+ if PY3: -+ name = decode(name) -+ # XXX - gets changed later and probably needs refactoring -+ return name -+ -+ def exe(self): -+ try: -+ return os.readlink("%s/%s/exe" % (self._procfs_path, self.pid)) -+ except OSError as err: -+ if err.errno in (errno.ENOENT, errno.ESRCH): -+ # no such file error; might be raised also if the -+ # path actually exists for system processes with -+ # low pids (about 0-20) -+ if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): -+ return "" -+ else: -+ if not _psposix.pid_exists(self.pid): -+ raise NoSuchProcess(self.pid, self._name) -+ else: -+ raise ZombieProcess(self.pid, self._name, self._ppid) -+ if err.errno in (errno.EPERM, errno.EACCES): -+ raise AccessDenied(self.pid, self._name) -+ raise -+ -+ @wrap_exceptions -+ def cmdline(self): -+ with open_text("%s/%s/cmdline" % (self._procfs_path, self.pid)) as f: -+ data = f.read() -+ if not data: -+ # may happen in case of zombie process -+ return [] -+ if data.endswith('\x00'): -+ data = data[:-1] -+ return [x for x in data.split('\x00')] -+ -+ # TODO: /proc//environ will be available in newer versions of -+ # Cygwin--do a version check and provide it if available. -+ -+ @wrap_exceptions -+ def terminal(self): -+ tty_nr = int(self._parse_stat_file()[5]) -+ tmap = _psposix.get_terminal_map() -+ try: -+ return tmap[tty_nr] -+ except KeyError: -+ return None -+ -+ @wrap_exceptions -+ def io_counters(self): -+ try: -+ ret = cext.proc_io_counters(self._winpid) -+ except OSError as err: -+ if err.errno in ACCESS_DENIED_SET: -+ nt = ntpinfo(*self.oneshot_info()) -+ ret = (nt.io_rcount, nt.io_wcount, nt.io_rbytes, nt.io_wbytes) -+ else: -+ raise -+ return _common.pio(*ret) -+ -+ @wrap_exceptions -+ def cpu_times(self): -+ values = self._parse_stat_file() -+ utime = float(values[12]) / CLOCK_TICKS -+ stime = float(values[13]) / CLOCK_TICKS -+ children_utime = float(values[14]) / CLOCK_TICKS -+ children_stime = float(values[15]) / CLOCK_TICKS -+ return _common.pcputimes(utime, stime, children_utime, children_stime) -+ -+ @wrap_exceptions -+ def wait(self, timeout=None): -+ try: -+ return _psposix.wait_pid(self.pid, timeout) -+ except _psposix.TimeoutExpired: -+ raise TimeoutExpired(timeout, self.pid, self._name) -+ -+ @wrap_exceptions -+ def create_time(self): -+ try: -+ return cext.proc_create_time(self._winpid) -+ except OSError as err: -+ if err.errno in ACCESS_DENIED_SET: -+ return ntpinfo(*self.oneshot_info()).create_time -+ raise -+ -+ def _get_raw_meminfo(self): -+ try: -+ return cext.proc_memory_info(self._winpid) -+ except OSError as err: -+ if err.errno in ACCESS_DENIED_SET: -+ # TODO: the C ext can probably be refactored in order -+ # to get this from cext.proc_info() -+ return cext.proc_memory_info_2(self._winpid) -+ raise -+ -+ @wrap_exceptions -+ def memory_info(self): -+ # on Windows RSS == WorkingSetSize and VMS == PagefileUsage. -+ # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS -+ # struct. -+ t = self._get_raw_meminfo() -+ rss = t[2] # wset -+ vms = t[7] # pagefile -+ return pmem(*(rss, vms, ) + t) -+ -+ @wrap_exceptions -+ def memory_full_info(self): -+ basic_mem = self.memory_info() -+ uss = cext.proc_memory_uss(self._winpid) -+ return pfullmem(*basic_mem + (uss, )) -+ -+ def memory_maps(self): -+ try: -+ raw = cext.proc_memory_maps(self._winpid) -+ except OSError as err: -+ # XXX - can't use wrap_exceptions decorator as we're -+ # returning a generator; probably needs refactoring. -+ if err.errno in ACCESS_DENIED_SET: -+ raise AccessDenied(self.pid, self._name) -+ if err.errno == errno.ESRCH: -+ raise NoSuchProcess(self.pid, self._name) -+ raise -+ else: -+ for addr, perm, path, rss in raw: -+ path = cext.winpath_to_cygpath(convert_dos_path(path)) -+ addr = hex(addr) -+ yield (addr, perm, path, rss) -+ -+ @wrap_exceptions -+ def cwd(self): -+ return os.readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) -+ -+ @wrap_exceptions -+ def num_ctx_switches(self): -+ ctx_switches = ntpinfo(*self.oneshot_info()).ctx_switches -+ # only voluntary ctx switches are supported -+ return _common.pctxsw(ctx_switches, 0) -+ -+ @wrap_exceptions -+ def num_threads(self): -+ return ntpinfo(*self.oneshot_info()).num_threads -+ -+ @wrap_exceptions -+ def threads(self): -+ rawlist = cext.proc_threads(self._winpid) -+ retlist = [] -+ for thread_id, utime, stime in rawlist: -+ ntuple = _common.pthread(thread_id, utime, stime) -+ retlist.append(ntuple) -+ return retlist -+ -+ @wrap_exceptions -+ def nice_get(self): -+ # Use C implementation -+ return cext_posix.getpriority(self.pid) -+ -+ @wrap_exceptions -+ def nice_set(self, value): -+ return cext_posix.setpriority(self.pid, value) -+ -+ @wrap_exceptions -+ def cpu_affinity_get(self): -+ def from_bitmask(x): -+ return [i for i in xrange(64) if (1 << i) & x] -+ bitmask = cext.proc_cpu_affinity_get(self._winpid) -+ return from_bitmask(bitmask) -+ -+ @wrap_exceptions -+ def cpu_affinity_set(self, value): -+ def to_bitmask(l): -+ if not l: -+ raise ValueError("invalid argument %r" % l) -+ out = 0 -+ for bit in l: -+ out |= 2 ** bit -+ return out -+ -+ # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER -+ # is returned for an invalid CPU but this seems not to be true, -+ # therefore we check CPUs validy beforehand. -+ allcpus = list(range(len(per_cpu_times()))) -+ for cpu in value: -+ if cpu not in allcpus: -+ if not isinstance(cpu, (int, long)): -+ raise TypeError( -+ "invalid CPU %r; an integer is required" % cpu) -+ else: -+ raise ValueError("invalid CPU %r" % cpu) -+ -+ bitmask = to_bitmask(value) -+ try: -+ cext.proc_cpu_affinity_set(self._winpid, bitmask) -+ except OSError as exc: -+ if exc.errno == errno.EIO: -+ raise AccessDenied(self.pid, self._name) -+ -+ @wrap_exceptions -+ def status(self): -+ letter = self._parse_stat_file()[1] -+ if PY3: -+ letter = letter.decode() -+ # XXX is '?' legit? (we're not supposed to return it anyway) -+ return PROC_STATUSES.get(letter, '?') -+ -+ @wrap_exceptions -+ def open_files(self): -+ retlist = [] -+ files = os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)) -+ hit_enoent = False -+ for fd in files: -+ file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd) -+ try: -+ path = os.readlink(file) -+ except OSError as err: -+ # ENOENT == file which is gone in the meantime -+ if err.errno in (errno.ENOENT, errno.ESRCH): -+ hit_enoent = True -+ continue -+ elif err.errno == errno.EINVAL: -+ # not a link -+ continue -+ else: -+ raise -+ else: -+ # If path is not an absolute there's no way to tell -+ # whether it's a regular file or not, so we skip it. -+ # A regular file is always supposed to be have an -+ # absolute path though. -+ if path.startswith('/') and isfile_strict(path): -+ ntuple = popenfile(path, int(fd)) -+ retlist.append(ntuple) -+ if hit_enoent: -+ # raise NSP if the process disappeared on us -+ os.stat('%s/%s' % (self._procfs_path, self.pid)) -+ return retlist -+ -+ @wrap_exceptions -+ def connections(self, kind='inet'): -+ return net_connections(kind, _pid=self.pid) -+ -+ @wrap_exceptions -+ def num_fds(self): -+ return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))) -+ -+ @wrap_exceptions -+ def ppid(self): -+ return int(self._parse_stat_file()[2]) -+ -+ @wrap_exceptions -+ def uids(self, _uids_re=re.compile(b'Uid:\t(\d+)')): -+ # More or less the same as on Linux, but the fields are separated by -+ # spaces instead of tabs; and anyways there is no difference on Cygwin -+ # between real, effective, and saved uids. -+ # TODO: We could use the same regexp on both Linux and Cygwin by just -+ # changing the Linux regexp to treat whitespace more flexibly -+ data = self._read_status_file() -+ real = _uids_re.findall(data)[0] -+ return _common.puids(int(real), int(real), int(real)) -+ -+ @wrap_exceptions -+ def gids(self, _gids_re=re.compile(b'Gid:\t(\d+)')): -+ # See note in uids -+ data = self._read_status_file() -+ real = _gids_re.findall(data)[0] -+ return _common.pgids(int(real), int(real), int(real)) -diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py -index 533b548..51852b1 100644 ---- a/psutil/_pslinux.py -+++ b/psutil/_pslinux.py -@@ -9,7 +9,6 @@ from __future__ import division - import base64 - import collections - import errno --import functools - import glob - import os - import re -@@ -25,9 +24,13 @@ from . import _common - from . import _psposix - from . import _psutil_linux as cext - from . import _psutil_posix as cext_posix -+from ._common import decode -+from ._common import get_procfs_path - from ._common import isfile_strict - from ._common import memoize - from ._common import memoize_when_activated -+from ._common import open_binary -+from ._common import open_text - from ._common import parse_environ_block - from ._common import NIC_DUPLEX_FULL - from ._common import NIC_DUPLEX_HALF -@@ -35,6 +38,7 @@ from ._common import NIC_DUPLEX_UNKNOWN - from ._common import path_exists_strict - from ._common import supports_ipv6 - from ._common import usage_percent -+from ._common import wrap_exceptions - from ._compat import b - from ._compat import basestring - from ._compat import long -@@ -83,9 +87,7 @@ BOOT_TIME = None # set later - # speedup, see: https://github.com/giampaolo/psutil/issues/708 - BIGGER_FILE_BUFFERING = -1 if PY3 else 8192 - LITTLE_ENDIAN = sys.byteorder == 'little' --if PY3: -- FS_ENCODING = sys.getfilesystemencoding() -- ENCODING_ERRORS_HANDLER = 'surrogateescape' -+ - if enum is None: - AF_LINK = socket.AF_PACKET - else: -@@ -186,37 +188,6 @@ pio = namedtuple('pio', ['read_count', 'write_count', - # ===================================================================== - - --def open_binary(fname, **kwargs): -- return open(fname, "rb", **kwargs) -- -- --def open_text(fname, **kwargs): -- """On Python 3 opens a file in text mode by using fs encoding and -- a proper en/decoding errors handler. -- On Python 2 this is just an alias for open(name, 'rt'). -- """ -- if PY3: -- # See: -- # https://github.com/giampaolo/psutil/issues/675 -- # https://github.com/giampaolo/psutil/pull/733 -- kwargs.setdefault('encoding', FS_ENCODING) -- kwargs.setdefault('errors', ENCODING_ERRORS_HANDLER) -- return open(fname, "rt", **kwargs) -- -- --if PY3: -- def decode(s): -- return s.decode(encoding=FS_ENCODING, errors=ENCODING_ERRORS_HANDLER) --else: -- def decode(s): -- return s -- -- --def get_procfs_path(): -- """Return updated psutil.PROCFS_PATH constant.""" -- return sys.modules['psutil'].PROCFS_PATH -- -- - def readlink(path): - """Wrapper around os.readlink().""" - assert isinstance(path, basestring), path -@@ -1304,26 +1275,6 @@ def pid_exists(pid): - return pid in pids() - - --def wrap_exceptions(fun): -- """Decorator which translates bare OSError and IOError exceptions -- into NoSuchProcess and AccessDenied. -- """ -- @functools.wraps(fun) -- def wrapper(self, *args, **kwargs): -- try: -- return fun(self, *args, **kwargs) -- except EnvironmentError as err: -- # ENOENT (no such file or directory) gets raised on open(). -- # ESRCH (no such process) can get raised on read() if -- # process is gone in meantime. -- if err.errno in (errno.ENOENT, errno.ESRCH): -- raise NoSuchProcess(self.pid, self._name) -- if err.errno in (errno.EPERM, errno.EACCES): -- raise AccessDenied(self.pid, self._name) -- raise -- return wrapper -- -- - class Process(object): - """Linux process implementation.""" - -diff --git a/psutil/_psposix.py b/psutil/_psposix.py -index 6ed7694..5b79a42 100644 ---- a/psutil/_psposix.py -+++ b/psutil/_psposix.py -@@ -173,12 +173,16 @@ def get_terminal_map(): - Used by Process.terminal() - """ - ret = {} -- ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') -+ # NOTE: On Cygwin pseudo-ttys are mapped to /dev/ptyN or /dev/consN -+ ls = (glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') + -+ glob.glob('/dev/pty*') + glob.glob('/dev/cons*')) - for name in ls: - assert name not in ret, name - try: - ret[os.stat(name).st_rdev] = name - except OSError as err: -- if err.errno != errno.ENOENT: -+ if err.errno not in (errno.ENOENT, errno.ENXIO): -+ # Note: ENXIO can happen in some cases on Cygwin due to a -+ # bug; see: https://cygwin.com/ml/cygwin/2017-08/msg00198.html - raise - return ret -diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py -index ad72de2..9269ca9 100644 ---- a/psutil/_pssunos.py -+++ b/psutil/_pssunos.py -@@ -15,6 +15,7 @@ from . import _common - from . import _psposix - from . import _psutil_posix as cext_posix - from . import _psutil_sunos as cext -+from ._common import get_procfs_path - from ._common import isfile_strict - from ._common import memoize_when_activated - from ._common import sockfam_to_enum -@@ -97,13 +98,15 @@ pmmap_ext = namedtuple( - - - # ===================================================================== --# --- utils -+# --- exceptions - # ===================================================================== - - --def get_procfs_path(): -- """Return updated psutil.PROCFS_PATH constant.""" -- return sys.modules['psutil'].PROCFS_PATH -+# these get overwritten on "import psutil" from the __init__.py file -+NoSuchProcess = None -+ZombieProcess = None -+AccessDenied = None -+TimeoutExpired = None - - - # ===================================================================== -diff --git a/psutil/_psutil_cygwin.c b/psutil/_psutil_cygwin.c -new file mode 100644 -index 0000000..e2f9e94 ---- /dev/null -+++ b/psutil/_psutil_cygwin.c -@@ -0,0 +1,2035 @@ -+#define WIN32_LEAN_AND_MEAN -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* On Cygwin, mprapi.h is missing a necessary include of wincrypt.h -+ * which is needed to define some types, so we include it here since -+ * iphlpapi.h includes iprtrmib.h which in turn includes mprapi.h -+ */ -+#include -+ -+/* TODO: There are some structs defined in netioapi.h that are only defined in -+ * ws2ipdef.h has been included; however, for reasons unknown to me currently, -+ * the headers included with Cygwin deliberately do not include ws2ipdef.h -+ * when compiling for Cygwin. For now I include it manually which seems to work -+ * but it would be good to track down why this was in the first place. -+ */ -+#include -+#include -+ -+#include -+ -+#include -+ -+#include -+#include -+ -+#include "arch/windows/process_info.h" -+#include "_psutil_common.h" -+ -+ -+/* -+ * ============================================================================ -+ * Utilities -+ * ============================================================================ -+ */ -+ -+ // a flag for connections without an actual status -+static int PSUTIL_CONN_NONE = 128; -+#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) -+#define _psutil_conn_decref_objs() \ -+ Py_DECREF(_AF_INET); \ -+ Py_DECREF(_AF_INET6);\ -+ Py_DECREF(_SOCK_STREAM);\ -+ Py_DECREF(_SOCK_DGRAM); -+ -+ -+// fix for mingw32, see -+// https://github.com/giampaolo/psutil/issues/351#c2 -+typedef struct _DISK_PERFORMANCE_WIN_2008 { -+ LARGE_INTEGER BytesRead; -+ LARGE_INTEGER BytesWritten; -+ LARGE_INTEGER ReadTime; -+ LARGE_INTEGER WriteTime; -+ LARGE_INTEGER IdleTime; -+ DWORD ReadCount; -+ DWORD WriteCount; -+ DWORD QueueDepth; -+ DWORD SplitCount; -+ LARGE_INTEGER QueryTime; -+ DWORD StorageDeviceNumber; -+ WCHAR StorageManagerName[8]; -+} DISK_PERFORMANCE_WIN_2008; -+ -+/* Python wrappers for Cygwin's cygwin_conv_path API--accepts and returns -+ * Python unicode strings. Always returns absolute paths. -+ */ -+static PyObject * -+psutil_cygwin_conv_path(PyObject *self, PyObject *args, -+ cygwin_conv_path_t what) { -+ char *from; -+ char *to; -+ ssize_t size; -+ -+ if (!PyArg_ParseTuple(args, "s", &from)) { -+ return NULL; -+ } -+ -+ size = cygwin_conv_path(what, from, NULL, 0); -+ -+ if (size < 0) { -+ /* TODO: Better error handling */ -+ return size; -+ } -+ -+ to = malloc(size); -+ if (to == NULL) { -+ return NULL; -+ } -+ -+ if (cygwin_conv_path(what, from, to, size)) { -+ return NULL; -+ } -+ -+ /* size includes the terminal null byte */ -+#if PY_MAJOR_VERSION >= 3 -+ return PyUnicode_FromStringAndSize(to, size - 1); -+#else -+ return PyString_FromStringAndSize(to, size - 1); -+#endif -+} -+ -+ -+static PyObject * -+psutil_cygpath_to_winpath(PyObject *self, PyObject *args) { -+ return psutil_cygwin_conv_path(self, args, CCP_POSIX_TO_WIN_A); -+} -+ -+ -+static PyObject * -+psutil_winpath_to_cygpath(PyObject *self, PyObject *args) { -+ return psutil_cygwin_conv_path(self, args, CCP_WIN_A_TO_POSIX); -+} -+ -+ -+/* TODO: Copied almost verbatim (_tcscmp -> wcscmp, _stprintf_s -> snprintf, -+ * _countof -> sizeof) so consider moving this into arch/windows or something -+ */ -+/* -+ Accept a filename's drive in native format like "\Device\HarddiskVolume1\" -+ and return the corresponding drive letter (e.g. "C:\\"). -+ If no match is found return an empty string. -+*/ -+static PyObject * -+psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) { -+ LPCTSTR lpDevicePath; -+ TCHAR d = TEXT('A'); -+ TCHAR szBuff[5]; -+ -+ if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) -+ return NULL; -+ -+ while (d <= TEXT('Z')) { -+ TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')}; -+ TCHAR szTarget[512] = {0}; -+ if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) { -+ if (wcscmp(lpDevicePath, szTarget) == 0) { -+ snprintf(szBuff, sizeof(szBuff), TEXT("%c:"), d); -+ return Py_BuildValue("s", szBuff); -+ } -+ } -+ d++; -+ } -+ return Py_BuildValue("s", ""); -+} -+ -+ -+static ULONGLONG (*psutil_GetTickCount64)(void) = NULL; -+ -+/* -+ * Return a Python float representing the system uptime expressed in seconds -+ * since the epoch. -+ */ -+static PyObject * -+psutil_boot_time(PyObject *self, PyObject *args) { -+ double uptime; -+ double pt; -+ FILETIME fileTime; -+ long long ll; -+ HINSTANCE hKernel32; -+ psutil_GetTickCount64 = NULL; -+ -+ GetSystemTimeAsFileTime(&fileTime); -+ -+ /* -+ HUGE thanks to: -+ http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry -+ -+ This function converts the FILETIME structure to a 32-bit Unix time. -+ The Unix time is a 32-bit value for the number of seconds since -+ January 1, 1970. A FILETIME is a 64-bit for the number of -+ 100-nanosecond periods since January 1, 1601. Convert by -+ subtracting the number of 100-nanosecond period betwee 01-01-1970 -+ and 01-01-1601, from time_t the divide by 1e+7 to get to the same -+ base granularity. -+ */ -+ ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) \ -+ + fileTime.dwLowDateTime; -+ pt = (double)(ll - 116444736000000000ull) / 1e7; -+ -+ // GetTickCount64() is Windows Vista+ only. Dinamically load -+ // GetTickCount64() at runtime. We may have used -+ // "#if (_WIN32_WINNT >= 0x0600)" pre-processor but that way -+ // the produced exe/wheels cannot be used on Windows XP, see: -+ // https://github.com/giampaolo/psutil/issues/811#issuecomment-230639178 -+ hKernel32 = GetModuleHandleW(L"KERNEL32"); -+ psutil_GetTickCount64 = (void*)GetProcAddress(hKernel32, "GetTickCount64"); -+ if (psutil_GetTickCount64 != NULL) { -+ // Windows >= Vista -+ uptime = (double)psutil_GetTickCount64() / 1000.00; -+ } -+ else { -+ // Windows XP. -+ // GetTickCount() time will wrap around to zero if the -+ // system is run continuously for 49.7 days. -+ uptime = (double)GetTickCount() / 1000.00; -+ } -+ -+ return Py_BuildValue("d", floor(pt - uptime)); -+} -+ -+ -+/* TODO: Copied verbatim from the Linux module; refactor */ -+/* -+ * Return disk mounted partitions as a list of tuples including device, -+ * mount point and filesystem type -+ */ -+static PyObject * -+psutil_disk_partitions(PyObject *self, PyObject *args) { -+ FILE *file = NULL; -+ struct mntent *entry; -+ PyObject *py_retlist = PyList_New(0); -+ PyObject *py_tuple = NULL; -+ -+ if (py_retlist == NULL) -+ return NULL; -+ -+ // MOUNTED constant comes from mntent.h and it's == '/etc/mtab' -+ Py_BEGIN_ALLOW_THREADS -+ file = setmntent(MOUNTED, "r"); -+ Py_END_ALLOW_THREADS -+ if ((file == 0) || (file == NULL)) { -+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, MOUNTED); -+ goto error; -+ } -+ -+ while ((entry = getmntent(file))) { -+ if (entry == NULL) { -+ PyErr_Format(PyExc_RuntimeError, "getmntent() failed"); -+ goto error; -+ } -+ py_tuple = Py_BuildValue("(ssss)", -+ entry->mnt_fsname, // device -+ entry->mnt_dir, // mount point -+ entry->mnt_type, // fs type -+ entry->mnt_opts); // options -+ if (! py_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_tuple)) -+ goto error; -+ Py_DECREF(py_tuple); -+ } -+ endmntent(file); -+ return py_retlist; -+ -+error: -+ if (file != NULL) -+ endmntent(file); -+ Py_XDECREF(py_tuple); -+ Py_DECREF(py_retlist); -+ return NULL; -+} -+ -+ -+/* Copied verbatim from the Windows module -+ TODO: Refactor this later -+ */ -+/* -+ * Return process CPU affinity as a bitmask -+ */ -+static PyObject * -+psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { -+ DWORD pid; -+ HANDLE hProcess; -+ DWORD_PTR proc_mask; -+ DWORD_PTR system_mask; -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ hProcess = psutil_handle_from_pid(pid); -+ if (hProcess == NULL) -+ return NULL; -+ -+ if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) { -+ CloseHandle(hProcess); -+ return PyErr_SetFromWindowsErr(0); -+ } -+ -+ CloseHandle(hProcess); -+#ifdef _WIN64 -+ return Py_BuildValue("K", (unsigned long long)proc_mask); -+#else -+ return Py_BuildValue("k", (unsigned long)proc_mask); -+#endif -+} -+ -+ -+/* -+ * Return process memory information as a Python tuple. -+ */ -+static PyObject * -+psutil_proc_memory_info(PyObject *self, PyObject *args) { -+ HANDLE hProcess; -+ DWORD pid; -+#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 -+ PROCESS_MEMORY_COUNTERS_EX cnt; -+#else -+ PROCESS_MEMORY_COUNTERS cnt; -+#endif -+ SIZE_T private = 0; -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ -+ hProcess = psutil_handle_from_pid(pid); -+ if (NULL == hProcess) -+ return NULL; -+ -+ if (! GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt, -+ sizeof(cnt))) { -+ CloseHandle(hProcess); -+ return PyErr_SetFromWindowsErr(0); -+ } -+ -+#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 -+ private = cnt.PrivateUsage; -+#endif -+ -+ CloseHandle(hProcess); -+ -+ // PROCESS_MEMORY_COUNTERS values are defined as SIZE_T which on 64bits -+ // is an (unsigned long long) and on 32bits is an (unsigned int). -+ // "_WIN64" is defined if we're running a 64bit Python interpreter not -+ // exclusively if the *system* is 64bit. -+#if defined(_WIN64) -+ return Py_BuildValue( -+ "(kKKKKKKKKK)", -+ cnt.PageFaultCount, // unsigned long -+ (unsigned long long)cnt.PeakWorkingSetSize, -+ (unsigned long long)cnt.WorkingSetSize, -+ (unsigned long long)cnt.QuotaPeakPagedPoolUsage, -+ (unsigned long long)cnt.QuotaPagedPoolUsage, -+ (unsigned long long)cnt.QuotaPeakNonPagedPoolUsage, -+ (unsigned long long)cnt.QuotaNonPagedPoolUsage, -+ (unsigned long long)cnt.PagefileUsage, -+ (unsigned long long)cnt.PeakPagefileUsage, -+ (unsigned long long)private); -+#else -+ return Py_BuildValue( -+ "(kIIIIIIIII)", -+ cnt.PageFaultCount, // unsigned long -+ (unsigned int)cnt.PeakWorkingSetSize, -+ (unsigned int)cnt.WorkingSetSize, -+ (unsigned int)cnt.QuotaPeakPagedPoolUsage, -+ (unsigned int)cnt.QuotaPagedPoolUsage, -+ (unsigned int)cnt.QuotaPeakNonPagedPoolUsage, -+ (unsigned int)cnt.QuotaNonPagedPoolUsage, -+ (unsigned int)cnt.PagefileUsage, -+ (unsigned int)cnt.PeakPagefileUsage, -+ (unsigned int)private); -+#endif -+} -+ -+ -+/* -+ * Alternative implementation of the one above but bypasses ACCESS DENIED. -+ */ -+static PyObject * -+psutil_proc_memory_info_2(PyObject *self, PyObject *args) { -+ DWORD pid; -+ PSYSTEM_PROCESS_INFORMATION process; -+ PVOID buffer; -+ SIZE_T private; -+ unsigned long pfault_count; -+ -+#if defined(_WIN64) -+ unsigned long long m1, m2, m3, m4, m5, m6, m7, m8; -+#else -+ unsigned int m1, m2, m3, m4, m5, m6, m7, m8; -+#endif -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ if (! psutil_get_proc_info(pid, &process, &buffer)) -+ return NULL; -+ -+#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 -+ private = process->PrivatePageCount; -+#else -+ private = 0; -+#endif -+ pfault_count = process->PageFaultCount; -+ -+ m1 = process->PeakWorkingSetSize; -+ m2 = process->WorkingSetSize; -+ m3 = process->QuotaPeakPagedPoolUsage; -+ m4 = process->QuotaPagedPoolUsage; -+ m5 = process->QuotaPeakNonPagedPoolUsage; -+ m6 = process->QuotaNonPagedPoolUsage; -+ m7 = process->PagefileUsage; -+ m8 = process->PeakPagefileUsage; -+ -+ free(buffer); -+ -+ // SYSTEM_PROCESS_INFORMATION values are defined as SIZE_T which on 64 -+ // bits is an (unsigned long long) and on 32bits is an (unsigned int). -+ // "_WIN64" is defined if we're running a 64bit Python interpreter not -+ // exclusively if the *system* is 64bit. -+#if defined(_WIN64) -+ return Py_BuildValue("(kKKKKKKKKK)", -+#else -+ return Py_BuildValue("(kIIIIIIIII)", -+#endif -+ pfault_count, m1, m2, m3, m4, m5, m6, m7, m8, private); -+} -+ -+ -+/** -+ * Returns the USS of the process. -+ * Reference: -+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/ -+ * nsMemoryReporterManager.cpp -+ */ -+static PyObject * -+psutil_proc_memory_uss(PyObject *self, PyObject *args) -+{ -+ DWORD pid; -+ HANDLE proc; -+ PSAPI_WORKING_SET_INFORMATION tmp; -+ DWORD tmp_size = sizeof(tmp); -+ size_t entries; -+ size_t private_pages; -+ size_t i; -+ DWORD info_array_size; -+ PSAPI_WORKING_SET_INFORMATION* info_array; -+ SYSTEM_INFO system_info; -+ PyObject* py_result = NULL; -+ unsigned long long total = 0; -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ -+ proc = psutil_handle_from_pid(pid); -+ if (proc == NULL) -+ return NULL; -+ -+ // Determine how many entries we need. -+ memset(&tmp, 0, tmp_size); -+ if (!QueryWorkingSet(proc, &tmp, tmp_size)) { -+ // NB: QueryWorkingSet is expected to fail here due to the -+ // buffer being too small. -+ if (tmp.NumberOfEntries == 0) { -+ PyErr_SetFromWindowsErr(0); -+ goto done; -+ } -+ } -+ -+ // Fudge the size in case new entries are added between calls. -+ entries = tmp.NumberOfEntries * 2; -+ -+ if (!entries) { -+ goto done; -+ } -+ -+ info_array_size = tmp_size + (entries * sizeof(PSAPI_WORKING_SET_BLOCK)); -+ info_array = (PSAPI_WORKING_SET_INFORMATION*)malloc(info_array_size); -+ if (!info_array) { -+ PyErr_NoMemory(); -+ goto done; -+ } -+ -+ if (!QueryWorkingSet(proc, info_array, info_array_size)) { -+ PyErr_SetFromWindowsErr(0); -+ goto done; -+ } -+ -+ entries = (size_t)info_array->NumberOfEntries; -+ private_pages = 0; -+ for (i = 0; i < entries; i++) { -+ // Count shared pages that only one process is using as private. -+ if (!info_array->WorkingSetInfo[i].Shared || -+ info_array->WorkingSetInfo[i].ShareCount <= 1) { -+ private_pages++; -+ } -+ } -+ -+ // GetSystemInfo has no return value. -+ GetSystemInfo(&system_info); -+ total = private_pages * system_info.dwPageSize; -+ py_result = Py_BuildValue("K", total); -+ -+done: -+ if (proc) { -+ CloseHandle(proc); -+ } -+ -+ if (info_array) { -+ free(info_array); -+ } -+ -+ return py_result; -+} -+ -+ -+/* -+ * Set process CPU affinity -+ */ -+static PyObject * -+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { -+ DWORD pid; -+ HANDLE hProcess; -+ DWORD dwDesiredAccess = \ -+ PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; -+ DWORD_PTR mask; -+ -+#ifdef _WIN64 -+ if (! PyArg_ParseTuple(args, "lK", &pid, &mask)) -+#else -+ if (! PyArg_ParseTuple(args, "lk", &pid, &mask)) -+#endif -+ { -+ return NULL; -+ } -+ hProcess = psutil_handle_from_pid_waccess(pid, dwDesiredAccess); -+ if (hProcess == NULL) -+ return NULL; -+ -+ if (SetProcessAffinityMask(hProcess, mask) == 0) { -+ CloseHandle(hProcess); -+ PyErr_SetFromWindowsErr(0); -+ return NULL; -+ } -+ -+ CloseHandle(hProcess); -+ Py_RETURN_NONE; -+} -+ -+ -+/* -+ * Return a Python float indicating the process create time expressed in -+ * seconds since the epoch. -+ */ -+static PyObject * -+psutil_proc_create_time(PyObject *self, PyObject *args) { -+ long pid; -+ long long unix_time; -+ DWORD exitCode; -+ HANDLE hProcess; -+ BOOL ret; -+ FILETIME ftCreate, ftExit, ftKernel, ftUser; -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ -+ // special case for PIDs 0 and 4, return system boot time -+ if (0 == pid || 4 == pid) -+ return psutil_boot_time(NULL, NULL); -+ -+ hProcess = psutil_handle_from_pid(pid); -+ if (hProcess == NULL) -+ return NULL; -+ -+ if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { -+ CloseHandle(hProcess); -+ printf("GetLastError() = %d\n", GetLastError()); -+ if (GetLastError() == ERROR_ACCESS_DENIED) { -+ // usually means the process has died so we throw a -+ // NoSuchProcess here -+ return NoSuchProcess(); -+ } -+ else { -+ PyErr_SetFromWindowsErr(0); -+ return NULL; -+ } -+ } -+ -+ // Make sure the process is not gone as OpenProcess alone seems to be -+ // unreliable in doing so (it seems a previous call to p.wait() makes -+ // it unreliable). -+ // This check is important as creation time is used to make sure the -+ // process is still running. -+ ret = GetExitCodeProcess(hProcess, &exitCode); -+ CloseHandle(hProcess); -+ if (ret != 0) { -+ if (exitCode != STILL_ACTIVE) -+ return NoSuchProcess(); -+ } -+ else { -+ // Ignore access denied as it means the process is still alive. -+ // For all other errors, we want an exception. -+ if (GetLastError() != ERROR_ACCESS_DENIED) { -+ PyErr_SetFromWindowsErr(0); -+ return NULL; -+ } -+ } -+ -+ /* -+ Convert the FILETIME structure to a Unix time. -+ It's the best I could find by googling and borrowing code here and there. -+ The time returned has a precision of 1 second. -+ */ -+ unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; -+ unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; -+ unix_time /= 10000000; -+ return Py_BuildValue("d", (double)unix_time); -+} -+ -+ -+// TODO: Copied verbatim from the windows module -+/* -+ * Get various process information by using NtQuerySystemInformation. -+ * We use this as a fallback when faster functions fail with access -+ * denied. This is slower because it iterates over all processes. -+ * Returned tuple includes the following process info: -+ * -+ * - num_threads -+ * - ctx_switches -+ * - num_handles (fallback) -+ * - user/kernel times (fallback) -+ * - create time (fallback) -+ * - io counters (fallback) -+ */ -+static PyObject * -+psutil_proc_info(PyObject *self, PyObject *args) { -+ DWORD pid; -+ PSYSTEM_PROCESS_INFORMATION process; -+ PVOID buffer; -+ ULONG num_handles; -+ ULONG i; -+ ULONG ctx_switches = 0; -+ double user_time; -+ double kernel_time; -+ long long create_time; -+ int num_threads; -+ LONGLONG io_rcount, io_wcount, io_rbytes, io_wbytes; -+ -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ if (! psutil_get_proc_info(pid, &process, &buffer)) -+ return NULL; -+ -+ num_handles = process->HandleCount; -+ for (i = 0; i < process->NumberOfThreads; i++) { -+ // The updated headers in mingw-w64 appear to be using an older version -+ // of this API in which the ContextSwitches member is still called -+ // Reserved3 -+#if __MINGW64_VERSION_MAJOR < 7 -+ // Using the copy of this struct definition included in psutil -+ ctx_switches += process->Threads[i].ContextSwitches; -+#else -+ // Using the definition from mingw-w64-w32api-headers -+ ctx_switches += process->Threads[i].Reserved3; -+#endif -+ } -+ user_time = (double)process->UserTime.HighPart * 429.4967296 + \ -+ (double)process->UserTime.LowPart * 1e-7; -+ kernel_time = (double)process->KernelTime.HighPart * 429.4967296 + \ -+ (double)process->KernelTime.LowPart * 1e-7; -+ // Convert the LARGE_INTEGER union to a Unix time. -+ // It's the best I could find by googling and borrowing code here -+ // and there. The time returned has a precision of 1 second. -+ if (0 == pid || 4 == pid) { -+ // the python module will translate this into BOOT_TIME later -+ create_time = 0; -+ } -+ else { -+ create_time = ((LONGLONG)process->CreateTime.HighPart) << 32; -+ create_time += process->CreateTime.LowPart - 116444736000000000LL; -+ create_time /= 10000000; -+ } -+ num_threads = (int)process->NumberOfThreads; -+ io_rcount = process->ReadOperationCount.QuadPart; -+ io_wcount = process->WriteOperationCount.QuadPart; -+ io_rbytes = process->ReadTransferCount.QuadPart; -+ io_wbytes = process->WriteTransferCount.QuadPart; -+ free(buffer); -+ -+ return Py_BuildValue( -+ "kkdddiKKKK", -+ num_handles, -+ ctx_switches, -+ user_time, -+ kernel_time, -+ (double)create_time, -+ num_threads, -+ io_rcount, -+ io_wcount, -+ io_rbytes, -+ io_wbytes -+ ); -+} -+ -+ -+// TODO: Copied verbatim from windows module -+/* -+ * Return a Python tuple referencing process I/O counters. -+ */ -+static PyObject * -+psutil_proc_io_counters(PyObject *self, PyObject *args) { -+ DWORD pid; -+ HANDLE hProcess; -+ IO_COUNTERS IoCounters; -+ -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ return NULL; -+ hProcess = psutil_handle_from_pid(pid); -+ if (NULL == hProcess) -+ return NULL; -+ if (! GetProcessIoCounters(hProcess, &IoCounters)) { -+ CloseHandle(hProcess); -+ return PyErr_SetFromWindowsErr(0); -+ } -+ CloseHandle(hProcess); -+ return Py_BuildValue("(KKKK)", -+ IoCounters.ReadOperationCount, -+ IoCounters.WriteOperationCount, -+ IoCounters.ReadTransferCount, -+ IoCounters.WriteTransferCount); -+} -+ -+ -+static PyObject * -+psutil_proc_threads(PyObject *self, PyObject *args) { -+ HANDLE hThread; -+ THREADENTRY32 te32 = {0}; -+ long pid; -+ int pid_return; -+ int rc; -+ FILETIME ftDummy, ftKernel, ftUser; -+ HANDLE hThreadSnap = NULL; -+ PyObject *py_tuple = NULL; -+ PyObject *py_retlist = PyList_New(0); -+ -+ if (py_retlist == NULL) -+ return NULL; -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ goto error; -+ if (pid == 0) { -+ // raise AD instead of returning 0 as procexp is able to -+ // retrieve useful information somehow -+ AccessDenied(); -+ goto error; -+ } -+ -+ pid_return = psutil_pid_is_running(pid); -+ if (pid_return == 0) { -+ NoSuchProcess(); -+ goto error; -+ } -+ if (pid_return == -1) -+ goto error; -+ -+ hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); -+ if (hThreadSnap == INVALID_HANDLE_VALUE) { -+ PyErr_SetFromWindowsErr(0); -+ goto error; -+ } -+ -+ // Fill in the size of the structure before using it -+ te32.dwSize = sizeof(THREADENTRY32); -+ -+ if (! Thread32First(hThreadSnap, &te32)) { -+ PyErr_SetFromWindowsErr(0); -+ goto error; -+ } -+ -+ // Walk the thread snapshot to find all threads of the process. -+ // If the thread belongs to the process, increase the counter. -+ do { -+ if (te32.th32OwnerProcessID == pid) { -+ py_tuple = NULL; -+ hThread = NULL; -+ hThread = OpenThread(THREAD_QUERY_INFORMATION, -+ FALSE, te32.th32ThreadID); -+ if (hThread == NULL) { -+ // thread has disappeared on us -+ continue; -+ } -+ -+ rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, -+ &ftUser); -+ if (rc == 0) { -+ PyErr_SetFromWindowsErr(0); -+ goto error; -+ } -+ -+ /* -+ * User and kernel times are represented as a FILETIME structure -+ * wich contains a 64-bit value representing the number of -+ * 100-nanosecond intervals since January 1, 1601 (UTC): -+ * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx -+ * To convert it into a float representing the seconds that the -+ * process has executed in user/kernel mode I borrowed the code -+ * below from Python's Modules/posixmodule.c -+ */ -+ py_tuple = Py_BuildValue( -+ "kdd", -+ te32.th32ThreadID, -+ (double)(ftUser.dwHighDateTime * 429.4967296 + \ -+ ftUser.dwLowDateTime * 1e-7), -+ (double)(ftKernel.dwHighDateTime * 429.4967296 + \ -+ ftKernel.dwLowDateTime * 1e-7)); -+ if (!py_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_tuple)) -+ goto error; -+ Py_DECREF(py_tuple); -+ -+ CloseHandle(hThread); -+ } -+ } while (Thread32Next(hThreadSnap, &te32)); -+ -+ CloseHandle(hThreadSnap); -+ return py_retlist; -+ -+error: -+ Py_XDECREF(py_tuple); -+ Py_DECREF(py_retlist); -+ if (hThread != NULL) -+ CloseHandle(hThread); -+ if (hThreadSnap != NULL) -+ CloseHandle(hThreadSnap); -+ return NULL; -+} -+ -+ -+// TODO: This is copied almost verbatim from the Linux module, but on Cygwin -+// it's necessary to use the utmpx APIs in order to access some of the extended -+// utmp fields, such as ut_tv. -+/* -+ * Return currently connected users as a list of tuples. -+ */ -+static PyObject * -+psutil_users(PyObject *self, PyObject *args) { -+ struct utmpx *ut; -+ PyObject *py_retlist = PyList_New(0); -+ PyObject *py_tuple = NULL; -+ PyObject *py_user_proc = NULL; -+ -+ if (py_retlist == NULL) -+ return NULL; -+ setutxent(); -+ while (NULL != (ut = getutxent())) { -+ py_tuple = NULL; -+ py_user_proc = NULL; -+ if (ut->ut_type == USER_PROCESS) -+ py_user_proc = Py_True; -+ else -+ py_user_proc = Py_False; -+ py_tuple = Py_BuildValue( -+ "(sssfO)", -+ ut->ut_user, // username -+ ut->ut_line, // tty -+ ut->ut_host, // hostname -+ (float)ut->ut_tv.tv_sec, // tstamp -+ py_user_proc // (bool) user process -+ ); -+ if (! py_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_tuple)) -+ goto error; -+ Py_DECREF(py_tuple); -+ } -+ endutent(); -+ return py_retlist; -+ -+error: -+ Py_XDECREF(py_tuple); -+ Py_XDECREF(py_user_proc); -+ Py_DECREF(py_retlist); -+ endutent(); -+ return NULL; -+} -+ -+ -+PIP_ADAPTER_ADDRESSES -+psutil_get_nic_addresses(int all) { -+ // allocate a 15 KB buffer to start with -+ int outBufLen = 15000; -+ DWORD dwRetVal = 0; -+ ULONG attempts = 0; -+ PIP_ADAPTER_ADDRESSES pAddresses = NULL; -+ -+ do { -+ pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); -+ if (pAddresses == NULL) { -+ PyErr_NoMemory(); -+ return NULL; -+ } -+ -+ dwRetVal = GetAdaptersAddresses( -+ AF_UNSPEC, -+ all ? GAA_FLAG_INCLUDE_ALL_INTERFACES : 0, -+ NULL, pAddresses, -+ &outBufLen); -+ if (dwRetVal == ERROR_BUFFER_OVERFLOW) { -+ free(pAddresses); -+ pAddresses = NULL; -+ } -+ else { -+ break; -+ } -+ -+ attempts++; -+ } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); -+ -+ if (dwRetVal != NO_ERROR) { -+ PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed."); -+ return NULL; -+ } -+ -+ return pAddresses; -+} -+ -+ -+/* -+ * Provides stats about NIC interfaces installed on the system. -+ * TODO: get 'duplex' (currently it's hard coded to '2', aka -+ 'full duplex') -+ */ -+ -+/* TODO: This and the helper get_nic_addresses are copied *almost* verbatim -+ from the windows module. One difference is the use of snprintf with -+ the %ls format, as opposed to using sprintf_s from MSCRT -+ The other difference is that get_nic_addresses() returns all interfaces, -+ */ -+static PyObject * -+psutil_net_if_stats(PyObject *self, PyObject *args) { -+ int i; -+ DWORD dwSize = 0; -+ DWORD dwRetVal = 0; -+ MIB_IFTABLE *pIfTable; -+ MIB_IFROW *pIfRow; -+ PIP_ADAPTER_ADDRESSES pAddresses = NULL; -+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; -+ char descr[MAX_PATH]; -+ int ifname_found; -+ -+ PyObject *py_nic_name = NULL; -+ PyObject *py_retdict = PyDict_New(); -+ PyObject *py_ifc_info = NULL; -+ PyObject *py_is_up = NULL; -+ -+ if (py_retdict == NULL) -+ return NULL; -+ -+ pAddresses = psutil_get_nic_addresses(1); -+ if (pAddresses == NULL) -+ goto error; -+ -+ pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); -+ if (pIfTable == NULL) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ dwSize = sizeof(MIB_IFTABLE); -+ if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { -+ free(pIfTable); -+ pIfTable = (MIB_IFTABLE *) malloc(dwSize); -+ if (pIfTable == NULL) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ } -+ // Make a second call to GetIfTable to get the actual -+ // data we want. -+ if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) { -+ PyErr_SetString(PyExc_RuntimeError, "GetIfTable() failed"); -+ goto error; -+ } -+ -+ for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { -+ pIfRow = (MIB_IFROW *) & pIfTable->table[i]; -+ -+ // GetIfTable is not able to give us NIC with "friendly names" -+ // so we determine them via GetAdapterAddresses() which -+ // provides friendly names *and* descriptions and find the -+ // ones that match. -+ ifname_found = 0; -+ pCurrAddresses = pAddresses; -+ while (pCurrAddresses) { -+ snprintf(descr, MAX_PATH, "%ls", pCurrAddresses->Description); -+ if (lstrcmp(descr, pIfRow->bDescr) == 0) { -+ py_nic_name = PyUnicode_FromWideChar( -+ pCurrAddresses->FriendlyName, -+ wcslen(pCurrAddresses->FriendlyName)); -+ if (py_nic_name == NULL) -+ goto error; -+ ifname_found = 1; -+ break; -+ } -+ pCurrAddresses = pCurrAddresses->Next; -+ } -+ if (ifname_found == 0) { -+ // Name not found means GetAdapterAddresses() doesn't list -+ // this NIC, only GetIfTable, meaning it's not really a NIC -+ // interface so we skip it. -+ continue; -+ } -+ -+ // is up? -+ if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || -+ pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && -+ pIfRow->dwAdminStatus == 1 ) { -+ py_is_up = Py_True; -+ } -+ else { -+ py_is_up = Py_False; -+ } -+ Py_INCREF(py_is_up); -+ -+ py_ifc_info = Py_BuildValue( -+ "(Oikk)", -+ py_is_up, -+ 2, // there's no way to know duplex so let's assume 'full' -+ pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb -+ pIfRow->dwMtu -+ ); -+ if (!py_ifc_info) -+ goto error; -+ if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info)) -+ goto error; -+ Py_DECREF(py_nic_name); -+ Py_DECREF(py_ifc_info); -+ } -+ -+ free(pIfTable); -+ free(pAddresses); -+ return py_retdict; -+ -+error: -+ Py_XDECREF(py_is_up); -+ Py_XDECREF(py_ifc_info); -+ Py_XDECREF(py_nic_name); -+ Py_DECREF(py_retdict); -+ if (pIfTable != NULL) -+ free(pIfTable); -+ if (pAddresses != NULL) -+ free(pAddresses); -+ return NULL; -+} -+ -+ -+/* -+ * Return a Python list of named tuples with overall network I/O information -+ */ -+static PyObject * -+psutil_net_io_counters(PyObject *self, PyObject *args) { -+ DWORD dwRetVal = 0; -+ -+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above -+ MIB_IF_ROW2 *pIfRow = NULL; -+#else // Windows XP -+ MIB_IFROW *pIfRow = NULL; -+#endif -+ -+ PIP_ADAPTER_ADDRESSES pAddresses = NULL; -+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; -+ PyObject *py_retdict = PyDict_New(); -+ PyObject *py_nic_info = NULL; -+ PyObject *py_nic_name = NULL; -+ -+ if (py_retdict == NULL) -+ return NULL; -+ pAddresses = psutil_get_nic_addresses(0); -+ if (pAddresses == NULL) -+ goto error; -+ pCurrAddresses = pAddresses; -+ -+ while (pCurrAddresses) { -+ py_nic_name = NULL; -+ py_nic_info = NULL; -+ -+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above -+ pIfRow = (MIB_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2)); -+#else // Windows XP -+ pIfRow = (MIB_IFROW *) malloc(sizeof(MIB_IFROW)); -+#endif -+ -+ if (pIfRow == NULL) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ -+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above -+ SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2)); -+ pIfRow->InterfaceIndex = pCurrAddresses->IfIndex; -+ dwRetVal = GetIfEntry2(pIfRow); -+#else // Windows XP -+ pIfRow->dwIndex = pCurrAddresses->IfIndex; -+ dwRetVal = GetIfEntry(pIfRow); -+#endif -+ -+ if (dwRetVal != NO_ERROR) { -+ PyErr_SetString(PyExc_RuntimeError, -+ "GetIfEntry() or GetIfEntry2() failed."); -+ goto error; -+ } -+ -+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above -+ py_nic_info = Py_BuildValue("(KKKKKKKK)", -+ pIfRow->OutOctets, -+ pIfRow->InOctets, -+ pIfRow->OutUcastPkts, -+ pIfRow->InUcastPkts, -+ pIfRow->InErrors, -+ pIfRow->OutErrors, -+ pIfRow->InDiscards, -+ pIfRow->OutDiscards); -+#else // Windows XP -+ py_nic_info = Py_BuildValue("(kkkkkkkk)", -+ pIfRow->dwOutOctets, -+ pIfRow->dwInOctets, -+ pIfRow->dwOutUcastPkts, -+ pIfRow->dwInUcastPkts, -+ pIfRow->dwInErrors, -+ pIfRow->dwOutErrors, -+ pIfRow->dwInDiscards, -+ pIfRow->dwOutDiscards); -+#endif -+ -+ if (!py_nic_info) -+ goto error; -+ -+ py_nic_name = PyUnicode_FromWideChar( -+ pCurrAddresses->FriendlyName, -+ wcslen(pCurrAddresses->FriendlyName)); -+ -+ if (py_nic_name == NULL) -+ goto error; -+ if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) -+ goto error; -+ Py_XDECREF(py_nic_name); -+ Py_XDECREF(py_nic_info); -+ -+ free(pIfRow); -+ pCurrAddresses = pCurrAddresses->Next; -+ } -+ -+ free(pAddresses); -+ return py_retdict; -+ -+error: -+ Py_XDECREF(py_nic_name); -+ Py_XDECREF(py_nic_info); -+ Py_DECREF(py_retdict); -+ if (pAddresses != NULL) -+ free(pAddresses); -+ if (pIfRow != NULL) -+ free(pIfRow); -+ return NULL; -+} -+ -+ -+// TODO: Copied verbatim from the windows module, so again the usual admonition -+// about refactoring -+/* -+ * Return a Python dict of tuples for disk I/O information -+ */ -+static PyObject * -+psutil_disk_io_counters(PyObject *self, PyObject *args) { -+ DISK_PERFORMANCE_WIN_2008 diskPerformance; -+ DWORD dwSize; -+ HANDLE hDevice = NULL; -+ char szDevice[MAX_PATH]; -+ char szDeviceDisplay[MAX_PATH]; -+ int devNum; -+ PyObject *py_retdict = PyDict_New(); -+ PyObject *py_tuple = NULL; -+ -+ if (py_retdict == NULL) -+ return NULL; -+ // Apparently there's no way to figure out how many times we have -+ // to iterate in order to find valid drives. -+ // Let's assume 32, which is higher than 26, the number of letters -+ // in the alphabet (from A:\ to Z:\). -+ for (devNum = 0; devNum <= 32; ++devNum) { -+ py_tuple = NULL; -+ snprintf(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum); -+ hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, -+ NULL, OPEN_EXISTING, 0, NULL); -+ -+ if (hDevice == INVALID_HANDLE_VALUE) -+ continue; -+ if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, -+ &diskPerformance, sizeof(diskPerformance), -+ &dwSize, NULL)) -+ { -+ snprintf(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum); -+ py_tuple = Py_BuildValue( -+ "(IILLKK)", -+ diskPerformance.ReadCount, -+ diskPerformance.WriteCount, -+ diskPerformance.BytesRead, -+ diskPerformance.BytesWritten, -+ (unsigned long long)(diskPerformance.ReadTime.QuadPart * 10) / 1000, -+ (unsigned long long)(diskPerformance.WriteTime.QuadPart * 10) / 1000); -+ if (!py_tuple) -+ goto error; -+ if (PyDict_SetItemString(py_retdict, szDeviceDisplay, -+ py_tuple)) -+ { -+ goto error; -+ } -+ Py_XDECREF(py_tuple); -+ } -+ else { -+ // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when -+ // compiling with mingw32; not sure what to do. -+ // return PyErr_SetFromWindowsErr(0); -+ ;; -+ } -+ -+ CloseHandle(hDevice); -+ } -+ -+ return py_retdict; -+ -+error: -+ Py_XDECREF(py_tuple); -+ Py_DECREF(py_retdict); -+ if (hDevice != NULL) -+ CloseHandle(hDevice); -+ return NULL; -+} -+ -+ -+// TODO: _GetExtended(Tcp|Udp)Table are copied straight out of the windows -+// module, and really ought to live somewhere in arch/windows -+ -+ -+typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG, -+ TCP_TABLE_CLASS, ULONG); -+ -+ -+// https://msdn.microsoft.com/library/aa365928.aspx -+static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call, -+ ULONG address_family, -+ PVOID * data, DWORD * size) -+{ -+ // Due to other processes being active on the machine, it's possible -+ // that the size of the table increases between the moment where we -+ // query the size and the moment where we query the data. Therefore, it's -+ // important to call this in a loop to retry if that happens. -+ // -+ // Also, since we may loop a theoretically unbounded number of times here, -+ // release the GIL while we're doing this. -+ DWORD error = ERROR_INSUFFICIENT_BUFFER; -+ *size = 0; -+ *data = NULL; -+ Py_BEGIN_ALLOW_THREADS; -+ error = call(NULL, size, FALSE, address_family, -+ TCP_TABLE_OWNER_PID_ALL, 0); -+ while (error == ERROR_INSUFFICIENT_BUFFER) -+ { -+ *data = malloc(*size); -+ if (*data == NULL) { -+ error = ERROR_NOT_ENOUGH_MEMORY; -+ continue; -+ } -+ error = call(*data, size, FALSE, address_family, -+ TCP_TABLE_OWNER_PID_ALL, 0); -+ if (error != NO_ERROR) { -+ free(*data); -+ *data = NULL; -+ } -+ } -+ Py_END_ALLOW_THREADS; -+ return error; -+} -+ -+ -+typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG, -+ UDP_TABLE_CLASS, ULONG); -+ -+ -+// https://msdn.microsoft.com/library/aa365930.aspx -+static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call, -+ ULONG address_family, -+ PVOID * data, DWORD * size) -+{ -+ // Due to other processes being active on the machine, it's possible -+ // that the size of the table increases between the moment where we -+ // query the size and the moment where we query the data. Therefore, it's -+ // important to call this in a loop to retry if that happens. -+ // -+ // Also, since we may loop a theoretically unbounded number of times here, -+ // release the GIL while we're doing this. -+ DWORD error = ERROR_INSUFFICIENT_BUFFER; -+ *size = 0; -+ *data = NULL; -+ Py_BEGIN_ALLOW_THREADS; -+ error = call(NULL, size, FALSE, address_family, -+ UDP_TABLE_OWNER_PID, 0); -+ while (error == ERROR_INSUFFICIENT_BUFFER) -+ { -+ *data = malloc(*size); -+ if (*data == NULL) { -+ error = ERROR_NOT_ENOUGH_MEMORY; -+ continue; -+ } -+ error = call(*data, size, FALSE, address_family, -+ UDP_TABLE_OWNER_PID, 0); -+ if (error != NO_ERROR) { -+ free(*data); -+ *data = NULL; -+ } -+ } -+ Py_END_ALLOW_THREADS; -+ return error; -+} -+ -+ -+/* -+ * Return a list of network connections opened by a process -+ */ -+static PyObject * -+psutil_net_connections(PyObject *self, PyObject *args) { -+ static long null_address[4] = { 0, 0, 0, 0 }; -+ unsigned long pid; -+ typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)(struct in_addr *, PSTR); -+ _RtlIpv4AddressToStringA rtlIpv4AddressToStringA; -+ typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)(struct in6_addr *, PSTR); -+ _RtlIpv6AddressToStringA rtlIpv6AddressToStringA; -+ _GetExtendedTcpTable getExtendedTcpTable; -+ _GetExtendedUdpTable getExtendedUdpTable; -+ PVOID table = NULL; -+ DWORD tableSize; -+ DWORD error; -+ PMIB_TCPTABLE_OWNER_PID tcp4Table; -+ PMIB_UDPTABLE_OWNER_PID udp4Table; -+ PMIB_TCP6TABLE_OWNER_PID tcp6Table; -+ PMIB_UDP6TABLE_OWNER_PID udp6Table; -+ ULONG i; -+ CHAR addressBufferLocal[65]; -+ CHAR addressBufferRemote[65]; -+ -+ PyObject *py_retlist; -+ PyObject *py_conn_tuple = NULL; -+ PyObject *py_af_filter = NULL; -+ PyObject *py_type_filter = NULL; -+ PyObject *py_addr_tuple_local = NULL; -+ PyObject *py_addr_tuple_remote = NULL; -+ PyObject *_AF_INET = PyLong_FromLong((long)AF_INET); -+ PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6); -+ PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM); -+ PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); -+ -+ if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) -+ { -+ _psutil_conn_decref_objs(); -+ return NULL; -+ } -+ -+ if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { -+ _psutil_conn_decref_objs(); -+ PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); -+ return NULL; -+ } -+ -+ if (pid != -1) { -+ if (psutil_pid_is_running(pid) == 0) { -+ _psutil_conn_decref_objs(); -+ return NoSuchProcess(); -+ } -+ } -+ -+ // Import some functions. -+ { -+ HMODULE ntdll; -+ HMODULE iphlpapi; -+ -+ ntdll = LoadLibrary(TEXT("ntdll.dll")); -+ rtlIpv4AddressToStringA = (_RtlIpv4AddressToStringA)GetProcAddress( -+ ntdll, "RtlIpv4AddressToStringA"); -+ rtlIpv6AddressToStringA = (_RtlIpv6AddressToStringA)GetProcAddress( -+ ntdll, "RtlIpv6AddressToStringA"); -+ /* TODO: Check these two function pointers */ -+ -+ iphlpapi = LoadLibrary(TEXT("iphlpapi.dll")); -+ getExtendedTcpTable = (_GetExtendedTcpTable)GetProcAddress(iphlpapi, -+ "GetExtendedTcpTable"); -+ getExtendedUdpTable = (_GetExtendedUdpTable)GetProcAddress(iphlpapi, -+ "GetExtendedUdpTable"); -+ FreeLibrary(ntdll); -+ FreeLibrary(iphlpapi); -+ } -+ -+ if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { -+ PyErr_SetString(PyExc_NotImplementedError, -+ "feature not supported on this Windows version"); -+ _psutil_conn_decref_objs(); -+ return NULL; -+ } -+ -+ py_retlist = PyList_New(0); -+ if (py_retlist == NULL) { -+ _psutil_conn_decref_objs(); -+ return NULL; -+ } -+ -+ // TCP IPv4 -+ -+ if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && -+ (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)) -+ { -+ table = NULL; -+ py_conn_tuple = NULL; -+ py_addr_tuple_local = NULL; -+ py_addr_tuple_remote = NULL; -+ tableSize = 0; -+ -+ error = __GetExtendedTcpTable(getExtendedTcpTable, -+ AF_INET, &table, &tableSize); -+ if (error == ERROR_NOT_ENOUGH_MEMORY) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ -+ if (error == NO_ERROR) -+ { -+ tcp4Table = table; -+ -+ for (i = 0; i < tcp4Table->dwNumEntries; i++) -+ { -+ if (pid != -1) { -+ if (tcp4Table->table[i].dwOwningPid != pid) { -+ continue; -+ } -+ } -+ -+ if (tcp4Table->table[i].dwLocalAddr != 0 || -+ tcp4Table->table[i].dwLocalPort != 0) -+ { -+ struct in_addr addr; -+ -+ addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; -+ rtlIpv4AddressToStringA(&addr, addressBufferLocal); -+ py_addr_tuple_local = Py_BuildValue( -+ "(si)", -+ addressBufferLocal, -+ BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); -+ } -+ else { -+ py_addr_tuple_local = PyTuple_New(0); -+ } -+ -+ if (py_addr_tuple_local == NULL) -+ goto error; -+ -+ // On Windows <= XP, remote addr is filled even if socket -+ // is in LISTEN mode in which case we just ignore it. -+ if ((tcp4Table->table[i].dwRemoteAddr != 0 || -+ tcp4Table->table[i].dwRemotePort != 0) && -+ (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) -+ { -+ struct in_addr addr; -+ -+ addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; -+ rtlIpv4AddressToStringA(&addr, addressBufferRemote); -+ py_addr_tuple_remote = Py_BuildValue( -+ "(si)", -+ addressBufferRemote, -+ BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); -+ } -+ else -+ { -+ py_addr_tuple_remote = PyTuple_New(0); -+ } -+ -+ if (py_addr_tuple_remote == NULL) -+ goto error; -+ -+ py_conn_tuple = Py_BuildValue( -+ "(iiiNNiI)", -+ -1, -+ AF_INET, -+ SOCK_STREAM, -+ py_addr_tuple_local, -+ py_addr_tuple_remote, -+ tcp4Table->table[i].dwState, -+ tcp4Table->table[i].dwOwningPid); -+ if (!py_conn_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_conn_tuple)) -+ goto error; -+ Py_DECREF(py_conn_tuple); -+ } -+ } -+ else { -+ PyErr_SetFromWindowsErr(error); -+ goto error; -+ } -+ -+ free(table); -+ table = NULL; -+ tableSize = 0; -+ } -+ -+ // TCP IPv6 -+ if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && -+ (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)) -+ { -+ table = NULL; -+ py_conn_tuple = NULL; -+ py_addr_tuple_local = NULL; -+ py_addr_tuple_remote = NULL; -+ tableSize = 0; -+ -+ error = __GetExtendedTcpTable(getExtendedTcpTable, -+ AF_INET6, &table, &tableSize); -+ if (error == ERROR_NOT_ENOUGH_MEMORY) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ -+ if (error == NO_ERROR) -+ { -+ tcp6Table = table; -+ -+ for (i = 0; i < tcp6Table->dwNumEntries; i++) -+ { -+ if (pid != -1) { -+ if (tcp6Table->table[i].dwOwningPid != pid) { -+ continue; -+ } -+ } -+ -+ if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) -+ != 0 || tcp6Table->table[i].dwLocalPort != 0) -+ { -+ struct in6_addr addr; -+ -+ memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); -+ rtlIpv6AddressToStringA(&addr, addressBufferLocal); -+ py_addr_tuple_local = Py_BuildValue( -+ "(si)", -+ addressBufferLocal, -+ BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); -+ } -+ else { -+ py_addr_tuple_local = PyTuple_New(0); -+ } -+ -+ if (py_addr_tuple_local == NULL) -+ goto error; -+ -+ // On Windows <= XP, remote addr is filled even if socket -+ // is in LISTEN mode in which case we just ignore it. -+ if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) -+ != 0 || -+ tcp6Table->table[i].dwRemotePort != 0) && -+ (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) -+ { -+ struct in6_addr addr; -+ -+ memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); -+ rtlIpv6AddressToStringA(&addr, addressBufferRemote); -+ py_addr_tuple_remote = Py_BuildValue( -+ "(si)", -+ addressBufferRemote, -+ BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); -+ } -+ else { -+ py_addr_tuple_remote = PyTuple_New(0); -+ } -+ -+ if (py_addr_tuple_remote == NULL) -+ goto error; -+ -+ py_conn_tuple = Py_BuildValue( -+ "(iiiNNiI)", -+ -1, -+ AF_INET6, -+ SOCK_STREAM, -+ py_addr_tuple_local, -+ py_addr_tuple_remote, -+ tcp6Table->table[i].dwState, -+ tcp6Table->table[i].dwOwningPid); -+ if (!py_conn_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_conn_tuple)) -+ goto error; -+ Py_DECREF(py_conn_tuple); -+ } -+ } -+ else { -+ PyErr_SetFromWindowsErr(error); -+ goto error; -+ } -+ -+ free(table); -+ table = NULL; -+ tableSize = 0; -+ } -+ -+ // UDP IPv4 -+ -+ if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && -+ (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)) -+ { -+ table = NULL; -+ py_conn_tuple = NULL; -+ py_addr_tuple_local = NULL; -+ py_addr_tuple_remote = NULL; -+ tableSize = 0; -+ error = __GetExtendedUdpTable(getExtendedUdpTable, -+ AF_INET, &table, &tableSize); -+ if (error == ERROR_NOT_ENOUGH_MEMORY) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ -+ if (error == NO_ERROR) -+ { -+ udp4Table = table; -+ -+ for (i = 0; i < udp4Table->dwNumEntries; i++) -+ { -+ if (pid != -1) { -+ if (udp4Table->table[i].dwOwningPid != pid) { -+ continue; -+ } -+ } -+ -+ if (udp4Table->table[i].dwLocalAddr != 0 || -+ udp4Table->table[i].dwLocalPort != 0) -+ { -+ struct in_addr addr; -+ -+ addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; -+ rtlIpv4AddressToStringA(&addr, addressBufferLocal); -+ py_addr_tuple_local = Py_BuildValue( -+ "(si)", -+ addressBufferLocal, -+ BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); -+ } -+ else { -+ py_addr_tuple_local = PyTuple_New(0); -+ } -+ -+ if (py_addr_tuple_local == NULL) -+ goto error; -+ -+ py_conn_tuple = Py_BuildValue( -+ "(iiiNNiI)", -+ -1, -+ AF_INET, -+ SOCK_DGRAM, -+ py_addr_tuple_local, -+ PyTuple_New(0), -+ PSUTIL_CONN_NONE, -+ udp4Table->table[i].dwOwningPid); -+ if (!py_conn_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_conn_tuple)) -+ goto error; -+ Py_DECREF(py_conn_tuple); -+ } -+ } -+ else { -+ PyErr_SetFromWindowsErr(error); -+ goto error; -+ } -+ -+ free(table); -+ table = NULL; -+ tableSize = 0; -+ } -+ -+ // UDP IPv6 -+ -+ if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && -+ (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)) -+ { -+ table = NULL; -+ py_conn_tuple = NULL; -+ py_addr_tuple_local = NULL; -+ py_addr_tuple_remote = NULL; -+ tableSize = 0; -+ error = __GetExtendedUdpTable(getExtendedUdpTable, -+ AF_INET6, &table, &tableSize); -+ if (error == ERROR_NOT_ENOUGH_MEMORY) { -+ PyErr_NoMemory(); -+ goto error; -+ } -+ -+ if (error == NO_ERROR) -+ { -+ udp6Table = table; -+ -+ for (i = 0; i < udp6Table->dwNumEntries; i++) { -+ if (pid != -1) { -+ if (udp6Table->table[i].dwOwningPid != pid) { -+ continue; -+ } -+ } -+ -+ if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) -+ != 0 || udp6Table->table[i].dwLocalPort != 0) -+ { -+ struct in6_addr addr; -+ -+ memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); -+ rtlIpv6AddressToStringA(&addr, addressBufferLocal); -+ py_addr_tuple_local = Py_BuildValue( -+ "(si)", -+ addressBufferLocal, -+ BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); -+ } -+ else { -+ py_addr_tuple_local = PyTuple_New(0); -+ } -+ -+ if (py_addr_tuple_local == NULL) -+ goto error; -+ -+ py_conn_tuple = Py_BuildValue( -+ "(iiiNNiI)", -+ -1, -+ AF_INET6, -+ SOCK_DGRAM, -+ py_addr_tuple_local, -+ PyTuple_New(0), -+ PSUTIL_CONN_NONE, -+ udp6Table->table[i].dwOwningPid); -+ if (!py_conn_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_conn_tuple)) -+ goto error; -+ Py_DECREF(py_conn_tuple); -+ } -+ } -+ else { -+ PyErr_SetFromWindowsErr(error); -+ goto error; -+ } -+ -+ free(table); -+ table = NULL; -+ tableSize = 0; -+ } -+ -+ _psutil_conn_decref_objs(); -+ return py_retlist; -+ -+error: -+ _psutil_conn_decref_objs(); -+ Py_XDECREF(py_conn_tuple); -+ Py_XDECREF(py_addr_tuple_local); -+ Py_XDECREF(py_addr_tuple_remote); -+ Py_DECREF(py_retlist); -+ if (table != NULL) -+ free(table); -+ return NULL; -+} -+ -+ -+static char *get_region_protection_string(ULONG protection) { -+ switch (protection & 0xff) { -+ case PAGE_NOACCESS: -+ return ""; -+ case PAGE_READONLY: -+ return "r"; -+ case PAGE_READWRITE: -+ return "rw"; -+ case PAGE_WRITECOPY: -+ return "wc"; -+ case PAGE_EXECUTE: -+ return "x"; -+ case PAGE_EXECUTE_READ: -+ return "xr"; -+ case PAGE_EXECUTE_READWRITE: -+ return "xrw"; -+ case PAGE_EXECUTE_WRITECOPY: -+ return "xwc"; -+ default: -+ return "?"; -+ } -+} -+ -+ -+/* TODO: Copied verbatim from the windows module; this should be refactored -+ * as well -+ */ -+/* -+ * Return a list of process's memory mappings. -+ */ -+static PyObject * -+psutil_proc_memory_maps(PyObject *self, PyObject *args) { -+#ifdef _WIN64 -+ MEMORY_BASIC_INFORMATION64 basicInfo; -+#else -+ MEMORY_BASIC_INFORMATION basicInfo; -+#endif -+ DWORD pid; -+ HANDLE hProcess = NULL; -+ PVOID baseAddress; -+ PVOID previousAllocationBase; -+ CHAR mappedFileName[MAX_PATH]; -+ SYSTEM_INFO system_info; -+ LPVOID maxAddr; -+ PyObject *py_retlist = PyList_New(0); -+ PyObject *py_tuple = NULL; -+ -+ if (py_retlist == NULL) -+ return NULL; -+ if (! PyArg_ParseTuple(args, "l", &pid)) -+ goto error; -+ hProcess = psutil_handle_from_pid(pid); -+ if (NULL == hProcess) -+ goto error; -+ -+ GetSystemInfo(&system_info); -+ maxAddr = system_info.lpMaximumApplicationAddress; -+ baseAddress = NULL; -+ previousAllocationBase = NULL; -+ -+ while (VirtualQueryEx(hProcess, baseAddress, &basicInfo, -+ sizeof(MEMORY_BASIC_INFORMATION))) -+ { -+ py_tuple = NULL; -+ if (baseAddress > maxAddr) -+ break; -+ if (GetMappedFileNameA(hProcess, baseAddress, mappedFileName, -+ sizeof(mappedFileName))) -+ { -+#ifdef _WIN64 -+ py_tuple = Py_BuildValue( -+ "(KssI)", -+ (unsigned long long)baseAddress, -+#else -+ py_tuple = Py_BuildValue( -+ "(kssI)", -+ (unsigned long)baseAddress, -+#endif -+ get_region_protection_string(basicInfo.Protect), -+ mappedFileName, -+ basicInfo.RegionSize); -+ -+ if (!py_tuple) -+ goto error; -+ if (PyList_Append(py_retlist, py_tuple)) -+ goto error; -+ Py_DECREF(py_tuple); -+ } -+ previousAllocationBase = basicInfo.AllocationBase; -+ baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize; -+ } -+ -+ CloseHandle(hProcess); -+ return py_retlist; -+ -+error: -+ Py_XDECREF(py_tuple); -+ Py_DECREF(py_retlist); -+ if (hProcess != NULL) -+ CloseHandle(hProcess); -+ return NULL; -+} -+ -+ -+/* -+ * Return battery usage stats. -+ */ -+static PyObject * -+psutil_sensors_battery(PyObject *self, PyObject *args) { -+ SYSTEM_POWER_STATUS sps; -+ -+ if (GetSystemPowerStatus(&sps) == 0) { -+ PyErr_SetFromWindowsErr(0); -+ return NULL; -+ } -+ return Py_BuildValue( -+ "iiiI", -+ sps.ACLineStatus, // whether AC is connected: 0=no, 1=yes, 255=unknown -+ // status flag: -+ // 1, 2, 4 = high, low, critical -+ // 8 = charging -+ // 128 = no battery -+ sps.BatteryFlag, -+ sps.BatteryLifePercent, // percent -+ sps.BatteryLifeTime // remaining secs -+ ); -+} -+ -+ -+/* -+ * define the psutil C module methods and initialize the module. -+ */ -+static PyMethodDef -+PsutilMethods[] = { -+ {"cygpath_to_winpath", psutil_cygpath_to_winpath, METH_VARARGS, -+ "Convert a Cygwin path to a Windows path"}, -+ {"winpath_to_cygpath", psutil_winpath_to_cygpath, METH_VARARGS, -+ "Convert a Windows path to a Cygwin path"}, -+ {"boot_time", psutil_boot_time, METH_VARARGS, -+ "Return the system boot time expressed in seconds since the epoch."}, -+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS, -+ "Return disk mounted partitions as a list of tuples including " -+ "device, mount point and filesystem type"}, -+ {"net_connections", psutil_net_connections, METH_VARARGS, -+ "Return system-wide connections"}, -+ {"net_if_stats", psutil_net_if_stats, METH_VARARGS, -+ "Return NICs stats."}, -+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS, -+ "Return dict of tuples of networks I/O information."}, -+ {"sensors_battery", psutil_sensors_battery, METH_VARARGS, -+ "Return battery metrics usage."}, -+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS, -+ "Return dict of tuples of disks I/O information."}, -+ {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS, -+ "Return a tuple of process memory information"}, -+ {"proc_memory_info_2", psutil_proc_memory_info_2, METH_VARARGS, -+ "Alternate implementation"}, -+ {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS, -+ "Return the USS of the process"}, -+ {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, -+ "Return process CPU affinity as a bitmask."}, -+ {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, -+ "Set process CPU affinity."}, -+ {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS, -+ "Get process I/O counters."}, -+ {"proc_threads", psutil_proc_threads, METH_VARARGS, -+ "Return process threads information as a list of tuple"}, -+ {"proc_create_time", psutil_proc_create_time, METH_VARARGS, -+ "Return a float indicating the process create time expressed in " -+ "seconds since the epoch"}, -+ // --- alternative pinfo interface -+ {"proc_info", psutil_proc_info, METH_VARARGS, -+ "Various process information"}, -+ {"users", psutil_users, METH_VARARGS, -+ "Return currently connected users as a list of tuples"}, -+ {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, -+ "Return a list of process's memory mappings"}, -+ -+ // --- windows API bindings -+ {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS, -+ "QueryDosDevice binding"}, -+ {NULL, NULL, 0, NULL} -+}; -+ -+struct module_state { -+ PyObject *error; -+}; -+ -+#if PY_MAJOR_VERSION >= 3 -+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -+#else -+#define GETSTATE(m) (&_state) -+#endif -+ -+#if PY_MAJOR_VERSION >= 3 -+ -+static int -+psutil_cygwin_traverse(PyObject *m, visitproc visit, void *arg) { -+ Py_VISIT(GETSTATE(m)->error); -+ return 0; -+} -+ -+static int -+psutil_cygwin_clear(PyObject *m) { -+ Py_CLEAR(GETSTATE(m)->error); -+ return 0; -+} -+ -+static struct PyModuleDef moduledef = { -+ PyModuleDef_HEAD_INIT, -+ "psutil_cygwin", -+ NULL, -+ sizeof(struct module_state), -+ PsutilMethods, -+ NULL, -+ psutil_cygwin_traverse, -+ psutil_cygwin_clear, -+ NULL -+}; -+ -+#define INITERROR return NULL -+ -+PyMODINIT_FUNC PyInit__psutil_cygwin(void) -+ -+#else -+#define INITERROR return -+ -+void init_psutil_cygwin(void) -+#endif -+{ -+#if PY_MAJOR_VERSION >= 3 -+ PyObject *module = PyModule_Create(&moduledef); -+#else -+ PyObject *module = Py_InitModule("_psutil_cygwin", PsutilMethods); -+#endif -+ -+ PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); -+ -+ // TODO: Copied verbatim from the Windows module; there ought to be a -+ // a function implementing these constants that can be shared between -+ // the two modules... -+ // connection status constants -+ // http://msdn.microsoft.com/en-us/library/cc669305.aspx -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_CLOSED", MIB_TCP_STATE_CLOSED); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_CLOSING", MIB_TCP_STATE_CLOSING); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_CLOSE_WAIT", MIB_TCP_STATE_CLOSE_WAIT); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_LISTEN", MIB_TCP_STATE_LISTEN); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_ESTAB", MIB_TCP_STATE_ESTAB); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_SYN_SENT", MIB_TCP_STATE_SYN_SENT); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_SYN_RCVD", MIB_TCP_STATE_SYN_RCVD); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_FIN_WAIT1", MIB_TCP_STATE_FIN_WAIT1); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_FIN_WAIT2", MIB_TCP_STATE_FIN_WAIT2); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_LAST_ACK", MIB_TCP_STATE_LAST_ACK); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT); -+ PyModule_AddIntConstant( -+ module, "MIB_TCP_STATE_DELETE_TCB", MIB_TCP_STATE_DELETE_TCB); -+ PyModule_AddIntConstant( -+ module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); -+ -+ /* TODO: More Windows constants that are defined as module constants -+ * Used both in the cygwin module (for now) and the windows module */ -+ PyModule_AddIntConstant( -+ module, "ERROR_ACCESS_DENIED", ERROR_ACCESS_DENIED); -+ -+ if (module == NULL) -+ INITERROR; -+#if PY_MAJOR_VERSION >= 3 -+ return module; -+#endif -+} -diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c -index 707c55a..ce4970e 100644 ---- a/psutil/_psutil_posix.c -+++ b/psutil/_psutil_posix.c -@@ -34,6 +34,9 @@ - #elif defined(PSUTIL_SUNOS) - #include - #include -+#elif defined(PSUTIL_CYGWIN) -+ #include -+ #include - #endif - - -@@ -167,6 +170,7 @@ static PyObject* - psutil_net_if_addrs(PyObject* self, PyObject* args) { - struct ifaddrs *ifaddr, *ifa; - int family; -+ char *name; - - PyObject *py_retlist = PyList_New(0); - PyObject *py_tuple = NULL; -@@ -185,6 +189,14 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) { - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; -+#ifdef __CYGWIN__ -+ // Support using the Cygwin-specific ifaddrs_hwdata struct to get -+ // the interface's "friendly name" rather than its more opaque -+ // UUID name -+ name = ((struct ifaddrs_hwdata *) (ifa->ifa_data))->ifa_frndlyname.ifrf_friendlyname; -+#else -+ name = ifa->ifa_name; -+#endif - family = ifa->ifa_addr->sa_family; - py_address = psutil_convert_ipaddr(ifa->ifa_addr, family); - // If the primary address can't be determined just skip it. -@@ -218,7 +230,7 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) { - goto error; - py_tuple = Py_BuildValue( - "(siOOOO)", -- ifa->ifa_name, -+ name, - family, - py_address, - py_netmask, -diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py -index 0105d6c..dfa8e63 100644 ---- a/psutil/_pswindows.py -+++ b/psutil/_pswindows.py -@@ -32,6 +32,7 @@ except ImportError as err: - raise - - from ._common import conn_tmap -+from ._common import encode - from ._common import isfile_strict - from ._common import parse_environ_block - from ._common import sockfam_to_enum -@@ -188,21 +189,6 @@ def convert_dos_path(s): - return os.path.join(driveletter, s[len(rawdrive):]) - - --def py2_strencode(s, encoding=sys.getfilesystemencoding()): -- """Encode a string in the given encoding. Falls back on returning -- the string as is if it can't be encoded. -- """ -- if PY3 or isinstance(s, str): -- return s -- else: -- try: -- return s.encode(encoding) -- except UnicodeEncodeError: -- # Filesystem codec failed, return the plain unicode -- # string (this should never happen). -- return s -- -- - # ===================================================================== - # --- memory - # ===================================================================== -@@ -343,7 +329,7 @@ def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - ret = cext.net_if_stats() - for name, items in ret.items(): -- name = py2_strencode(name) -+ name = encode(name) - isup, duplex, speed, mtu = items - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) -@@ -356,7 +342,7 @@ def net_io_counters(): - installed on the system as a dict of raw tuples. - """ - ret = cext.net_io_counters() -- return dict([(py2_strencode(k), v) for k, v in ret.items()]) -+ return dict([(encode(k), v) for k, v in ret.items()]) - - - def net_if_addrs(): -@@ -364,7 +350,7 @@ def net_if_addrs(): - ret = [] - for items in cext.net_if_addrs(): - items = list(items) -- items[0] = py2_strencode(items[0]) -+ items[0] = encode(items[0]) - ret.append(items) - return ret - -@@ -410,7 +396,7 @@ def users(): - rawlist = cext.users() - for item in rawlist: - user, hostname, tstamp = item -- user = py2_strencode(user) -+ user = encode(user) - nt = _common.suser(user, None, hostname, tstamp) - retlist.append(nt) - return retlist -@@ -669,9 +655,9 @@ class Process(object): - try: - # Note: this will fail with AD for most PIDs owned - # by another user but it's faster. -- return py2_strencode(os.path.basename(self.exe())) -+ return encode(os.path.basename(self.exe())) - except AccessDenied: -- return py2_strencode(cext.proc_name(self.pid)) -+ return encode(cext.proc_name(self.pid)) - - @wrap_exceptions - def exe(self): -@@ -683,7 +669,7 @@ class Process(object): - # see https://github.com/giampaolo/psutil/issues/528 - if self.pid in (0, 4): - raise AccessDenied(self.pid, self._name) -- return py2_strencode(convert_dos_path(cext.proc_exe(self.pid))) -+ return encode(convert_dos_path(cext.proc_exe(self.pid))) - - @wrap_exceptions - def cmdline(self): -@@ -691,7 +677,7 @@ class Process(object): - if PY3: - return ret - else: -- return [py2_strencode(s) for s in ret] -+ return [encode(s) for s in ret] - - @wrap_exceptions - def environ(self): -@@ -838,7 +824,7 @@ class Process(object): - # return a normalized pathname since the native C function appends - # "\\" at the and of the path - path = cext.proc_cwd(self.pid) -- return py2_strencode(os.path.normpath(path)) -+ return encode(os.path.normpath(path)) - - @wrap_exceptions - def open_files(self): -@@ -854,7 +840,7 @@ class Process(object): - _file = convert_dos_path(_file) - if isfile_strict(_file): - if not PY3: -- _file = py2_strencode(_file) -+ _file = encode(_file) - ntuple = _common.popenfile(_file, -1) - ret.add(ntuple) - return list(ret) -diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h -index 1bbbf2a..0d50f5b 100644 ---- a/psutil/arch/windows/ntextapi.h -+++ b/psutil/arch/windows/ntextapi.h -@@ -121,6 +121,7 @@ typedef enum _KTHREAD_STATE { - } KTHREAD_STATE, *PKTHREAD_STATE; - - -+#ifndef __MINGW64_VERSION_MAJOR - typedef enum _KWAIT_REASON { - Executive = 0, - FreePage = 1, -@@ -168,7 +169,10 @@ typedef struct _CLIENT_ID { - HANDLE UniqueThread; - } CLIENT_ID, *PCLIENT_ID; - -+#endif - -+ -+#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 7 - typedef struct _SYSTEM_THREAD_INFORMATION { - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; -@@ -182,6 +186,7 @@ typedef struct _SYSTEM_THREAD_INFORMATION { - ULONG ThreadState; - KWAIT_REASON WaitReason; - } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; -+#endif - - - typedef struct _TEB *PTEB; -@@ -287,6 +292,7 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( - ); - - -+#ifndef __CYGWIN__ - typedef enum _PROCESSINFOCLASS2 { - _ProcessBasicInformation, - ProcessQuotaLimits, -@@ -338,4 +344,6 @@ typedef enum _PROCESSINFOCLASS2 { - #define ProcessImageFileName _ProcessImageFileName - #define ProcessBreakOnTermination _ProcessBreakOnTermination - -+#endif -+ - #endif // __NTEXTAPI_H__ -diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c -index e29f216..868f947 100644 ---- a/psutil/arch/windows/process_info.c -+++ b/psutil/arch/windows/process_info.c -@@ -39,6 +39,8 @@ psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) { - if (hProcess == NULL) { - if (GetLastError() == ERROR_INVALID_PARAMETER) - NoSuchProcess(); -+ else if (GetLastError() == ERROR_ACCESS_DENIED) -+ AccessDenied(); - else - PyErr_SetFromWindowsErr(0); - return NULL; -diff --git a/psutil/arch/windows/process_info.h b/psutil/arch/windows/process_info.h -index 7c2c9c2..3275372 100644 ---- a/psutil/arch/windows/process_info.h -+++ b/psutil/arch/windows/process_info.h -@@ -15,6 +15,9 @@ - #define HANDLE_TO_PYNUM(handle) PyLong_FromUnsignedLong((unsigned long) handle) - #define PYNUM_TO_HANDLE(obj) ((HANDLE)PyLong_AsUnsignedLong(obj)) - -+#ifdef PSUTIL_CYGWIN -+ #include "py_error.h" -+#endif - - DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs); - HANDLE psutil_handle_from_pid(DWORD pid); -diff --git a/psutil/arch/windows/py_error.c b/psutil/arch/windows/py_error.c -new file mode 100644 -index 0000000..c7a6bf6 ---- /dev/null -+++ b/psutil/arch/windows/py_error.c -@@ -0,0 +1,59 @@ -+#include -+#include -+ -+/* TODO: This does not actually work as intended per the comment further down -+ that the OSError constructor performs the Windows error to POSIX error -+ translation, and that logic probably does not exist in Cygwin's Python */ -+PyObject *PyErr_SetFromWindowsErr(int ierr) -+{ -+ int len; -+ WCHAR *s_buf = NULL; /* Free via LocalFree */ -+ PyObject *message; -+ PyObject *args, *v; -+ DWORD err = (DWORD)ierr; -+ if (err==0) err = GetLastError(); -+ len = FormatMessageW( -+ /* Error API error */ -+ FORMAT_MESSAGE_ALLOCATE_BUFFER | -+ FORMAT_MESSAGE_FROM_SYSTEM | -+ FORMAT_MESSAGE_IGNORE_INSERTS, -+ NULL, /* no message source */ -+ err, -+ MAKELANGID(LANG_NEUTRAL, -+ SUBLANG_DEFAULT), /* Default language */ -+ (LPWSTR) &s_buf, -+ 0, /* size not used */ -+ NULL); /* no args */ -+ if (len==0) { -+ /* Only seen this in out of mem situations */ -+ message = PyUnicode_FromFormat("Windows Error 0x%x", err); -+ s_buf = NULL; -+ } else { -+ /* remove trailing cr/lf and dots */ -+ while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.')) -+ s_buf[--len] = L'\0'; -+ message = PyUnicode_FromWideChar(s_buf, len); -+ } -+ -+ if (message == NULL) -+ { -+ LocalFree(s_buf); -+ return NULL; -+ } -+ -+ /* This is the constructor signature for OSError. -+ The POSIX translation will be figured out by the constructor. */ -+ args = Py_BuildValue("(iO)", err, message); -+ Py_DECREF(message); -+ -+ if (args != NULL) { -+ v = PyObject_Call(PyExc_OSError, args, NULL); -+ Py_DECREF(args); -+ if (v != NULL) { -+ PyErr_SetObject((PyObject *) Py_TYPE(v), v); -+ Py_DECREF(v); -+ } -+ } -+ LocalFree(s_buf); -+ return NULL; -+} -diff --git a/psutil/arch/windows/py_error.h b/psutil/arch/windows/py_error.h -new file mode 100644 -index 0000000..a3852b7 ---- /dev/null -+++ b/psutil/arch/windows/py_error.h -@@ -0,0 +1,8 @@ -+#ifndef __PY_ERROR_H__ -+#define __PY_ERROR_H__ -+ -+#include -+ -+PyObject *PyErr_SetFromWindowsErr(int); -+ -+#endif -diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py -index 13c4cfc..9db7235 100644 ---- a/psutil/tests/__init__.py -+++ b/psutil/tests/__init__.py -@@ -21,6 +21,7 @@ import socket - import stat - import subprocess - import sys -+import sysconfig - import tempfile - import textwrap - import threading -@@ -36,6 +37,7 @@ except ImportError: - import mock # NOQA - requires "pip install mock" - - import psutil -+from psutil import CYGWIN - from psutil import LINUX - from psutil import POSIX - from psutil import WINDOWS -@@ -101,6 +103,12 @@ GLOBAL_TIMEOUT = 3 - AF_INET6 = getattr(socket, "AF_INET6") - AF_UNIX = getattr(socket, "AF_UNIX", None) - PYTHON = os.path.realpath(sys.executable) -+EXE_SUFFIX = sysconfig.get_config_var('EXE') or '' -+ -+if CYGWIN and EXE_SUFFIX: -+ if PYTHON.endswith(EXE_SUFFIX): -+ PYTHON = PYTHON[:-len(EXE_SUFFIX)] -+ - DEVNULL = open(os.devnull, 'r+') - - TESTFILE_PREFIX = '$testfn' -@@ -182,6 +190,104 @@ class ThreadTask(threading.Thread): - - - # =================================================================== -+# --- sync primitives -+# =================================================================== -+ -+ -+class retry(object): -+ """A retry decorator.""" -+ -+ def __init__(self, -+ exception=Exception, -+ timeout=None, -+ retries=None, -+ interval=0.001, -+ logfun=lambda s: print(s, file=sys.stderr), -+ ): -+ if timeout and retries: -+ raise ValueError("timeout and retries args are mutually exclusive") -+ self.exception = exception -+ self.timeout = timeout -+ self.retries = retries -+ self.interval = interval -+ self.logfun = logfun -+ -+ def __iter__(self): -+ if self.timeout: -+ stop_at = time.time() + self.timeout -+ while time.time() < stop_at: -+ yield -+ elif self.retries: -+ for _ in range(self.retries): -+ yield -+ else: -+ while True: -+ yield -+ -+ def sleep(self): -+ if self.interval is not None: -+ time.sleep(self.interval) -+ -+ def __call__(self, fun): -+ @functools.wraps(fun) -+ def wrapper(*args, **kwargs): -+ exc = None -+ for _ in self: -+ try: -+ return fun(*args, **kwargs) -+ except self.exception as _: -+ exc = _ -+ if self.logfun is not None: -+ self.logfun(exc) -+ self.sleep() -+ else: -+ if PY3: -+ raise exc -+ else: -+ raise -+ -+ # This way the user of the decorated function can change config -+ # parameters. -+ wrapper.decorator = self -+ return wrapper -+ -+ -+@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT, -+ interval=0.001) -+def wait_for_pid(pid): -+ """Wait for pid to show up in the process list then return. -+ Used in the test suite to give time the sub process to initialize. -+ """ -+ psutil.Process(pid) -+ if WINDOWS: -+ # give it some more time to allow better initialization -+ time.sleep(0.01) -+ -+ -+@retry(exception=(EnvironmentError, AssertionError), logfun=None, -+ timeout=GLOBAL_TIMEOUT, interval=0.001) -+def wait_for_file(fname, delete_file=True, empty=False): -+ """Wait for a file to be written on disk with some content.""" -+ with open(fname, "rb") as f: -+ data = f.read() -+ if not empty: -+ assert data -+ if delete_file: -+ os.remove(fname) -+ return data -+ -+ -+@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT, -+ interval=0.001) -+def call_until(fun, expr): -+ """Keep calling function for timeout secs and exit if eval() -+ expression is True. -+ """ -+ ret = fun() -+ assert eval(expr) -+ return ret -+ -+# =================================================================== - # --- subprocesses - # =================================================================== - -@@ -189,6 +295,7 @@ class ThreadTask(threading.Thread): - _subprocesses_started = set() - - -+@retry(exception=EnvironmentError, timeout=GLOBAL_TIMEOUT, interval=1) - def get_test_subprocess(cmd=None, **kwds): - """Return a subprocess.Popen object to use in tests. - By default stdout and stderr are redirected to /dev/null and the -@@ -369,105 +476,6 @@ else: - - - # =================================================================== --# --- sync primitives --# =================================================================== -- -- --class retry(object): -- """A retry decorator.""" -- -- def __init__(self, -- exception=Exception, -- timeout=None, -- retries=None, -- interval=0.001, -- logfun=lambda s: print(s, file=sys.stderr), -- ): -- if timeout and retries: -- raise ValueError("timeout and retries args are mutually exclusive") -- self.exception = exception -- self.timeout = timeout -- self.retries = retries -- self.interval = interval -- self.logfun = logfun -- -- def __iter__(self): -- if self.timeout: -- stop_at = time.time() + self.timeout -- while time.time() < stop_at: -- yield -- elif self.retries: -- for _ in range(self.retries): -- yield -- else: -- while True: -- yield -- -- def sleep(self): -- if self.interval is not None: -- time.sleep(self.interval) -- -- def __call__(self, fun): -- @functools.wraps(fun) -- def wrapper(*args, **kwargs): -- exc = None -- for _ in self: -- try: -- return fun(*args, **kwargs) -- except self.exception as _: -- exc = _ -- if self.logfun is not None: -- self.logfun(exc) -- self.sleep() -- else: -- if PY3: -- raise exc -- else: -- raise -- -- # This way the user of the decorated function can change config -- # parameters. -- wrapper.decorator = self -- return wrapper -- -- --@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT, -- interval=0.001) --def wait_for_pid(pid): -- """Wait for pid to show up in the process list then return. -- Used in the test suite to give time the sub process to initialize. -- """ -- psutil.Process(pid) -- if WINDOWS: -- # give it some more time to allow better initialization -- time.sleep(0.01) -- -- --@retry(exception=(EnvironmentError, AssertionError), logfun=None, -- timeout=GLOBAL_TIMEOUT, interval=0.001) --def wait_for_file(fname, delete_file=True, empty=False): -- """Wait for a file to be written on disk with some content.""" -- with open(fname, "rb") as f: -- data = f.read() -- if not empty: -- assert data -- if delete_file: -- os.remove(fname) -- return data -- -- --@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT, -- interval=0.001) --def call_until(fun, expr): -- """Keep calling function for timeout secs and exit if eval() -- expression is True. -- """ -- ret = fun() -- assert eval(expr) -- return ret -- -- --# =================================================================== - # --- fs - # =================================================================== - -diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py -index 84215d3..1c10a71 100755 ---- a/psutil/tests/test_misc.py -+++ b/psutil/tests/test_misc.py -@@ -19,6 +19,7 @@ import socket - import stat - import sys - -+from psutil import CYGWIN - from psutil import LINUX - from psutil import NETBSD - from psutil import OPENBSD -@@ -442,7 +443,8 @@ class TestScripts(unittest.TestCase): - def test_pmap(self): - self.assert_stdout('pmap.py', args=str(os.getpid())) - -- @unittest.skipUnless(OSX or WINDOWS or LINUX, "platform not supported") -+ @unittest.skipUnless(OSX or WINDOWS or LINUX or CYGWIN, -+ "platform not supported") - def test_procsmem(self): - self.assert_stdout('procsmem.py') - -diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py -index 16d1eb7..0b934a8 100755 ---- a/psutil/tests/test_posix.py -+++ b/psutil/tests/test_posix.py -@@ -10,12 +10,14 @@ - import datetime - import errno - import os -+import re - import subprocess - import sys - import time - - import psutil - from psutil import BSD -+from psutil import CYGWIN - from psutil import LINUX - from psutil import OSX - from psutil import POSIX -@@ -37,25 +39,123 @@ from psutil.tests import unittest - from psutil.tests import wait_for_pid - - --def ps(cmd): -- """Expects a ps command with a -o argument and parse the result -- returning only the value of interest. -- """ -- if not LINUX: -- cmd = cmd.replace(" --no-headers ", " ") -- if SUNOS: -- cmd = cmd.replace("-o command", "-o comm") -- cmd = cmd.replace("-o start", "-o stime") -- p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) -+def run(cmd): -+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, -+ stderr=subprocess.PIPE) - output = p.communicate()[0].strip() -+ - if PY3: - output = str(output, sys.stdout.encoding) -- if not LINUX: -- output = output.split('\n')[1].strip() -- try: -- return int(output) -- except ValueError: -- return output -+ -+ return output -+ -+ -+def ps(fmt, pid=None): -+ """ -+ Wrapper for calling the ps command with a little bit of cross-platform -+ support for a narrow range of features. -+ """ -+ -+ # The ps on Cygwin bears only small resemblance to the *nix ps, and -+ # probably shouldn't even be used for these tests; this tries as -+ # best as possible to emulate it as used currently by these tests -+ cmd = ['ps'] -+ -+ if LINUX: -+ cmd.append('--no-headers') -+ -+ if pid is not None: -+ cmd.extend(['-p', str(pid)]) -+ else: -+ if SUNOS: -+ cmd.append('-A') -+ elif CYGWIN: -+ cmd.append('-a') -+ else: -+ cmd.append('ax') -+ -+ if SUNOS: -+ fmt_map = {'command', 'comm', -+ 'start', 'stime'} -+ fmt = fmt_map.get(fmt, fmt) -+ -+ if not CYGWIN: -+ cmd.extend(['-o', fmt]) -+ else: -+ cmd.append('-l') -+ -+ output = run(cmd) -+ -+ if LINUX: -+ output = output.splitlines() -+ else: -+ output = output.splitlines()[1:] -+ -+ if CYGWIN: -+ cygwin_ps_re = re.compile(r'I?\s*(?P\d+)\s*(?P\d+)\s*' -+ '(?P\d+)\s*(?P\d+)\s*' -+ '(?P[a-z0-9?]+)\s*(?P\d+)\s*' -+ '(?:(?P\d{2}:\d{2}:\d{2})|' -+ ' (?P[A-Za-z]+\s+\d+))\s*' -+ '(?P/.+)') -+ -+ def cygwin_output(line, fmt): -+ # NOTE: Cygwin's ps is very limited in what it outputs, so we work -+ # around that by looking to various other sources for some -+ # information -+ fmt_map = {'start': 'stime'} -+ fmt = fmt_map.get(fmt, fmt) -+ -+ m = cygwin_ps_re.match(line) -+ if not m: -+ return '' -+ -+ if fmt in cygwin_ps_re.groupindex: -+ output = m.group(fmt) -+ if output is None: -+ output = '' -+ elif fmt == 'rgid': -+ pid = m.group('pid') -+ output = open('/proc/{0}/gid'.format(pid)).readline().strip() -+ elif fmt == 'user': -+ # Cygwin's ps only returns UID -+ uid = m.group('uid') -+ output = run(['getent', 'passwd', uid]) -+ output = output.splitlines()[0].split(':')[0] -+ elif fmt == 'rss': -+ winpid = m.group('winpid') -+ output = run(['wmic', 'process', winpid, 'get', -+ 'WorkingSetSize']) -+ output = int(output.split('\n')[-1].strip()) / 1024 -+ elif fmt == 'vsz': -+ winpid = m.group('winpid') -+ output = run(['wmic', 'process', winpid, 'get', -+ 'PrivatePageCount']) -+ output = int(output.split('\n')[-1].strip()) / 1024 -+ else: -+ raise ValueError('format %s not supported on Cygwin' % fmt) -+ -+ return output -+ -+ all_output = [] -+ for line in output: -+ if CYGWIN: -+ output = cygwin_output(line, fmt) -+ -+ if not output: -+ continue -+ -+ try: -+ output = int(output) -+ except ValueError: -+ pass -+ -+ all_output.append(output) -+ -+ if pid is None: -+ return all_output -+ else: -+ return all_output[0] - - - @unittest.skipUnless(POSIX, "POSIX only") -@@ -75,49 +175,50 @@ class TestProcess(unittest.TestCase): - # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps - - def test_ppid(self): -- ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) -+ ppid_ps = ps('ppid', self.pid) - ppid_psutil = psutil.Process(self.pid).ppid() - self.assertEqual(ppid_ps, ppid_psutil) - - def test_uid(self): -- uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) -+ uid_ps = ps('uid', self.pid) - uid_psutil = psutil.Process(self.pid).uids().real - self.assertEqual(uid_ps, uid_psutil) - - def test_gid(self): -- gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) -+ gid_ps = ps('rgid', self.pid) - gid_psutil = psutil.Process(self.pid).gids().real - self.assertEqual(gid_ps, gid_psutil) - - def test_username(self): -- username_ps = ps("ps --no-headers -o user -p %s" % self.pid) -+ username_ps = ps('user', self.pid) - username_psutil = psutil.Process(self.pid).username() - self.assertEqual(username_ps, username_psutil) - -+ @unittest.skipIf(APPVEYOR and CYGWIN, "test not reliable on appveyor") - @skip_on_access_denied() - @retry_before_failing() - def test_rss_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) -- rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) -+ rss_ps = ps('rss', self.pid) - rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 - self.assertEqual(rss_ps, rss_psutil) - -+ @unittest.skipIf(APPVEYOR and CYGWIN, "test not reliable on appveyor") - @skip_on_access_denied() - @retry_before_failing() - def test_vsz_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) -- vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) -+ vsz_ps = ps('vsz', self.pid) - vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 - self.assertEqual(vsz_ps, vsz_psutil) - - def test_name(self): - # use command + arg since "comm" keyword not supported on all platforms -- name_ps = ps("ps --no-headers -o command -p %s" % ( -- self.pid)).split(' ')[0] -+ name_ps = ps('command', self.pid).split(' ')[0] - # remove path if there is any, from the command - name_ps = os.path.basename(name_ps).lower() - name_psutil = psutil.Process(self.pid).name().lower() -@@ -125,7 +226,7 @@ class TestProcess(unittest.TestCase): - - @unittest.skipIf(OSX or BSD, 'ps -o start not available') - def test_create_time(self): -- time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] -+ time_ps = ps('start', self.pid) - time_psutil = psutil.Process(self.pid).create_time() - time_psutil_tstamp = datetime.datetime.fromtimestamp( - time_psutil).strftime("%H:%M:%S") -@@ -137,8 +238,7 @@ class TestProcess(unittest.TestCase): - self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) - - def test_exe(self): -- ps_pathname = ps("ps --no-headers -o command -p %s" % -- self.pid).split(' ')[0] -+ ps_pathname = ps('command', self.pid).split(' ')[0] - psutil_pathname = psutil.Process(self.pid).exe() - try: - self.assertEqual(ps_pathname, psutil_pathname) -@@ -153,15 +253,27 @@ class TestProcess(unittest.TestCase): - self.assertEqual(ps_pathname, adjusted_ps_pathname) - - def test_cmdline(self): -- ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid) -+ ps_cmdline = ps('command', self.pid) - psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) -- if SUNOS: -- # ps on Solaris only shows the first part of the cmdline -+ if SUNOS or CYGWIN: -+ # ps on Solaris and Cygwin only shows the first part of the cmdline - psutil_cmdline = psutil_cmdline.split(" ")[0] -+ if CYGWIN: -+ # resolve symlinks -+ if os.path.islink(psutil_cmdline): -+ psutil_cmdline = os.path.splitext( -+ os.path.realpath(psutil_cmdline))[0] - self.assertEqual(ps_cmdline, psutil_cmdline) - -+ # To be more specific, process priorities are complicated in Windows and -+ # there's no simple way, from the command line, to get the information -+ # needed to reproduce the way Cygwin maps Windows process priorties to -+ # 'nice' values (not even through Cygwin's /proc API, which only returns -+ # the "base priority" which actually is not sufficient because what we -+ # really need is the process's "priority class" which is different -+ @unittest.skipIf(CYGWIN, "this is not supported by Cygwin") - def test_nice(self): -- ps_nice = ps("ps --no-headers -o nice -p %s" % self.pid) -+ ps_nice = ps('nice', self.pid) - psutil_nice = psutil.Process().nice() - self.assertEqual(ps_nice, psutil_nice) - -@@ -220,22 +332,7 @@ class TestSystemAPIs(unittest.TestCase): - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime -- if SUNOS: -- cmd = ["ps", "-A", "-o", "pid"] -- else: -- cmd = ["ps", "ax", "-o", "pid"] -- p = get_test_subprocess(cmd, stdout=subprocess.PIPE) -- output = p.communicate()[0].strip() -- assert p.poll() == 0 -- if PY3: -- output = str(output, sys.stdout.encoding) -- pids_ps = [] -- for line in output.split('\n')[1:]: -- if line: -- pid = int(line.split()[0].strip()) -- pids_ps.append(pid) -- # remove ps subprocess pid which is supposed to be dead in meantime -- pids_ps.remove(p.pid) -+ pids_ps = ps('pid') - pids_psutil = psutil.pids() - pids_ps.sort() - pids_psutil.sort() -@@ -244,7 +341,8 @@ class TestSystemAPIs(unittest.TestCase): - if OSX and 0 not in pids_ps: - pids_ps.insert(0, 0) - -- if pids_ps != pids_psutil: -+ # There will often be one more process in pids_ps for ps itself -+ if len(pids_ps) - len(pids_psutil) > 1: - difference = [x for x in pids_psutil if x not in pids_ps] + \ - [x for x in pids_ps if x not in pids_psutil] - self.fail("difference: " + str(difference)) -@@ -254,6 +352,11 @@ class TestSystemAPIs(unittest.TestCase): - @unittest.skipIf(SUNOS, "unreliable on SUNOS") - @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") - def test_nic_names(self): -+ if CYGWIN: -+ # On Cygwin perform the version of this test that uses ipconfig -+ from psutil.tests.test_windows import TestSystemAPIs -+ return TestSystemAPIs._test_nic_names(self) -+ - p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if p.returncode != 0: -@@ -274,14 +377,23 @@ class TestSystemAPIs(unittest.TestCase): - "unreliable on APPVEYOR or TRAVIS") - @retry_before_failing() - def test_users(self): -- out = sh("who") -- lines = out.split('\n') -- users = [x.split()[0] for x in lines] -- self.assertEqual(len(users), len(psutil.users())) -- terminals = [x.split()[1] for x in lines] -+ if CYGWIN: -+ # NOTE: For reasons I haven't been able to figure out (possibly a -+ # bug in Cygwin) `who` sometimes fails without explicitly -+ # specifying the utmp file. -+ out = sh("who /var/run/utmp") -+ else: -+ out = sh("who") -+ lines = [x.strip() for x in out.split('\n')] -+ self.assertEqual(len(lines), len(psutil.users())) - for u in psutil.users(): -- self.assertTrue(u.name in users, u.name) -- self.assertTrue(u.terminal in terminals, u.terminal) -+ for line in lines: -+ if line.startswith(u.name): -+ rest = line[len(u.name):].split() -+ if u.terminal == rest[0].strip(): -+ break -+ else: -+ self.fail("couldn't find %s in who output" % u.name) - - def test_pid_exists_let_raise(self): - # According to "man 2 kill" possible error values for kill -@@ -330,6 +442,9 @@ class TestSystemAPIs(unittest.TestCase): - - tolerance = 4 * 1024 * 1024 # 4MB - for part in psutil.disk_partitions(all=False): -+ if not os.path.exists(part.mountpoint): -+ continue -+ - usage = psutil.disk_usage(part.mountpoint) - try: - total, used, free, percent = df(part.device) -diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py -index 6580fe9..434cb3a 100755 ---- a/psutil/tests/test_process.py -+++ b/psutil/tests/test_process.py -@@ -29,6 +29,7 @@ from socket import SOCK_STREAM - import psutil - - from psutil import BSD -+from psutil import CYGWIN - from psutil import FREEBSD - from psutil import LINUX - from psutil import NETBSD -@@ -37,6 +38,7 @@ from psutil import OSX - from psutil import POSIX - from psutil import SUNOS - from psutil import WINDOWS -+from psutil._common import encode - from psutil._common import supports_ipv6 - from psutil._compat import callable - from psutil._compat import long -@@ -128,22 +130,30 @@ class TestProcess(unittest.TestCase): - self.assertFalse(psutil.pid_exists(p.pid)) - if POSIX: - self.assertEqual(exit_sig, -sig) -- # -- sproc = get_test_subprocess() -- p = psutil.Process(sproc.pid) -- p.send_signal(sig) -- with mock.patch('psutil.os.kill', -- side_effect=OSError(errno.ESRCH, "")): -- with self.assertRaises(psutil.NoSuchProcess): -- p.send_signal(sig) -- # -- sproc = get_test_subprocess() -- p = psutil.Process(sproc.pid) -- p.send_signal(sig) -- with mock.patch('psutil.os.kill', -- side_effect=OSError(errno.EPERM, "")): -- with self.assertRaises(psutil.AccessDenied): -- psutil.Process().send_signal(sig) -+ -+ if not CYGWIN: -+ # NOTE: This portion of the test is not reliable on Cygwin due -+ # to an apparent bug (?) in Cygwin that prevents zombie -+ # processes from remaining accessible before wait() in some -+ # cases. See -+ # https://www.cygwin.com/ml/cygwin/2017-02/msg00187.html -+ -+ sproc = get_test_subprocess() -+ p = psutil.Process(sproc.pid) -+ p.send_signal(sig) -+ with mock.patch('psutil.os.kill', -+ side_effect=OSError(errno.ESRCH, "")): -+ with self.assertRaises(psutil.NoSuchProcess): -+ p.send_signal(sig) -+ # -+ sproc = get_test_subprocess() -+ p = psutil.Process(sproc.pid) -+ p.send_signal(sig) -+ with mock.patch('psutil.os.kill', -+ side_effect=OSError(errno.EPERM, "")): -+ with self.assertRaises(psutil.AccessDenied): -+ psutil.Process().send_signal(sig) -+ - # Sending a signal to process with PID 0 is not allowed as - # it would affect every process in the process group of - # the calling process (os.getpid()) instead of PID 0"). -@@ -278,10 +288,18 @@ class TestProcess(unittest.TestCase): - # Use os.times()[:2] as base values to compare our results - # using a tolerance of +/- 0.1 seconds. - # It will fail if the difference between the values is > 0.1s. -- if (max([user_time, utime]) - min([user_time, utime])) > 0.1: -+ # On cygwin there seems to be enough overhead differece between -+ # os.times() and reading /proc/stat that the tolerance should -+ # be a bit higher -+ if CYGWIN: -+ tol = 0.2 -+ else: -+ tol = 0.1 -+ -+ if (max([user_time, utime]) - min([user_time, utime])) > tol: - self.fail("expected: %s, found: %s" % (utime, user_time)) - -- if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1: -+ if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > tol: - self.fail("expected: %s, found: %s" % (ktime, kernel_time)) - - @unittest.skipUnless(hasattr(psutil.Process, "cpu_num"), -@@ -317,7 +335,10 @@ class TestProcess(unittest.TestCase): - terminal = psutil.Process().terminal() - if sys.stdin.isatty() or sys.stdout.isatty(): - tty = os.path.realpath(sh('tty')) -- self.assertEqual(terminal, tty) -+ if CYGWIN and terminal == '/dev/console': -+ self.assertTrue(tty.startswith('/dev/cons')) -+ else: -+ self.assertEqual(terminal, tty) - else: - self.assertIsNone(terminal) - -@@ -1086,7 +1107,8 @@ class TestProcess(unittest.TestCase): - psutil.CONN_NONE, - ("all", "inet", "inet6", "udp", "udp6")) - -- @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'AF_UNIX not supported') -+ @unittest.skipUnless(hasattr(socket, 'AF_UNIX') and not CYGWIN, -+ 'AF_UNIX is not supported') - @skip_on_access_denied(only_if=OSX) - def test_connections_unix(self): - def check(type): -@@ -1115,6 +1137,8 @@ class TestProcess(unittest.TestCase): - 'socket.fromfd() not supported') - @unittest.skipIf(WINDOWS or SUNOS, - 'connection fd not available on this platform') -+ @unittest.skipIf(CYGWIN, -+ 'cannot map sockets to their fds on Cygwin') - def test_connection_fromfd(self): - with contextlib.closing(socket.socket()) as sock: - sock.bind(('localhost', 0)) -@@ -1191,8 +1215,10 @@ class TestProcess(unittest.TestCase): - for p in psutil.process_iter(): - if p.pid == sproc.pid: - continue -- # XXX: sometimes this fails on Windows; not sure why. -- self.assertNotEqual(p.ppid(), this_parent, msg=p) -+ try: -+ self.assertNotEqual(p.ppid(), this_parent, msg=p) -+ except psutil.AccessDenied: -+ pass - - def test_children(self): - p = psutil.Process() -@@ -1236,8 +1262,18 @@ class TestProcess(unittest.TestCase): - except psutil.Error: - pass - # this is the one, now let's make sure there are no duplicates -- pid = sorted(table.items(), key=lambda x: x[1])[-1][0] -- p = psutil.Process(pid) -+ for pid, _ in sorted(table.items(), key=lambda x: x[1], reverse=True): -+ try: -+ # Just make sure the process can be accessed and still actually -+ # exists (or exists in the first place--e.g. there is no such -+ # process with pid=1 on Cygwin even though it is the default -+ # ppid -+ p = psutil.Process(pid) -+ except (psutil.AccessDenied, psutil.NoSuchProcess): -+ continue -+ -+ break -+ - try: - c = p.children(recursive=True) - except psutil.AccessDenied: # windows -@@ -1351,7 +1387,7 @@ class TestProcess(unittest.TestCase): - p = psutil.Process(sproc.pid) - p.terminate() - p.wait() -- if WINDOWS: -+ if WINDOWS or CYGWIN: - call_until(psutil.pids, "%s not in ret" % p.pid) - self.assertFalse(p.is_running()) - # self.assertFalse(p.pid in psutil.pids(), msg="retcode = %s" % -@@ -1394,6 +1430,8 @@ class TestProcess(unittest.TestCase): - except psutil.AccessDenied: - if OPENBSD and name in ('threads', 'num_threads'): - pass -+ elif APPVEYOR and CYGWIN and name == 'cpu_affinity': -+ pass - else: - raise - except NotImplementedError: -@@ -1403,7 +1441,14 @@ class TestProcess(unittest.TestCase): - "NoSuchProcess exception not raised for %r, retval=%s" % ( - name, ret)) - -- @unittest.skipUnless(POSIX, 'POSIX only') -+ @unittest.skipUnless(POSIX and not CYGWIN, 'POSIX only') -+ # This test can't really work on Cygwin since some psutil interfaces -+ # (such as create_time, currently) rely on the Windows API, and while -+ # Cygwin does support zombie processes, the real Windows processes are -+ # already gone in that case, and the zombie "processes" only remain -+ # in Cygwin's internal process table -+ # TODO: In the future it would be nice to have cleaner handling of -+ # zombie processes on Cygwin - def test_zombie_process(self): - def succeed_or_zombie_p_exc(fun, *args, **kwargs): - try: -@@ -1806,17 +1851,17 @@ class TestFetchAllProcesses(unittest.TestCase): - def memory_info(self, ret, proc): - for name in ret._fields: - self.assertGreaterEqual(getattr(ret, name), 0) -- if POSIX and ret.vms != 0: -+ if WINDOWS or CYGWIN: -+ assert ret.peak_wset >= ret.wset, ret -+ assert ret.peak_paged_pool >= ret.paged_pool, ret -+ assert ret.peak_nonpaged_pool >= ret.nonpaged_pool, ret -+ assert ret.peak_pagefile >= ret.pagefile, ret -+ elif POSIX and ret.vms != 0: - # VMS is always supposed to be the highest - for name in ret._fields: - if name != 'vms': - value = getattr(ret, name) - assert ret.vms > value, ret -- elif WINDOWS: -- assert ret.peak_wset >= ret.wset, ret -- assert ret.peak_paged_pool >= ret.paged_pool, ret -- assert ret.peak_nonpaged_pool >= ret.nonpaged_pool, ret -- assert ret.peak_pagefile >= ret.pagefile, ret - - def memory_full_info(self, ret, proc): - total = psutil.virtual_memory().total -@@ -1855,6 +1900,11 @@ class TestFetchAllProcesses(unittest.TestCase): - - def cwd(self, ret, proc): - if ret is not None: # BSD may return None -+ if CYGWIN and ret == '': -+ # This can happen on Cygwin for processes that we can't access -+ # without elevation -+ return -+ - assert os.path.isabs(ret), ret - try: - st = os.stat(ret) -@@ -2025,9 +2075,7 @@ class TestUnicode(unittest.TestCase): - def test_proc_name(self): - subp = get_test_subprocess(cmd=[self.uexe]) - if WINDOWS: -- # XXX: why is this like this? -- from psutil._pswindows import py2_strencode -- name = py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)) -+ name = encode(psutil._psplatform.cext.proc_name(subp.pid)) - else: - name = psutil.Process(subp.pid).name() - if not OSX and TRAVIS: -@@ -2098,6 +2146,16 @@ class TestInvalidUnicode(TestUnicode): - uexe = TESTFN + b"f\xc0\x80" - udir = TESTFN + b"d\xc0\x80" - -+ # NOTE: Cygwin uses its own scheme for encoding characters in filenames -+ # that are not valid unicode codepoints (such as \x80) (specifically, it -+ # converts them to codepoints in a private use area, e.g. u+f080). So -+ # the filename ends up being reported as the utf-8 encoding of u+f080 -+ # This seems to be handled better on a problem on Python 3, however. -+ @unittest.skipIf(not PY3 and CYGWIN, -+ "Cygwin does not treat arbitrary bytes as on POSIX") -+ def test_proc_exe(self): -+ return super(TestInvalidUnicode, self).test_proc_exe() -+ - - if __name__ == '__main__': - run_test_module_by_name(__file__) -diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py -index 013ae8e..1daf41a 100755 ---- a/psutil/tests/test_system.py -+++ b/psutil/tests/test_system.py -@@ -20,6 +20,7 @@ import time - - import psutil - from psutil import BSD -+from psutil import CYGWIN - from psutil import FREEBSD - from psutil import LINUX - from psutil import NETBSD -@@ -469,7 +470,7 @@ class TestSystemAPIs(unittest.TestCase): - if SUNOS: - # on solaris apparently mount points can also be files - assert os.path.exists(disk.mountpoint), disk -- else: -+ elif not CYGWIN: - assert os.path.isdir(disk.mountpoint), disk - assert disk.fstype, disk - -@@ -477,7 +478,7 @@ class TestSystemAPIs(unittest.TestCase): - ls = psutil.disk_partitions(all=True) - self.assertTrue(ls, msg=ls) - for disk in psutil.disk_partitions(all=True): -- if not WINDOWS: -+ if not (WINDOWS or CYGWIN): - try: - os.stat(disk.mountpoint) - except OSError as err: -@@ -519,7 +520,7 @@ class TestSystemAPIs(unittest.TestCase): - - from psutil._common import conn_tmap - for kind, groups in conn_tmap.items(): -- if SUNOS and kind == 'unix': -+ if (SUNOS or CYGWIN) and kind == 'unix': - continue - families, types_ = groups - cons = psutil.net_connections(kind) -diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py -index 3fcc20e..ba25e28 100755 ---- a/psutil/tests/test_windows.py -+++ b/psutil/tests/test_windows.py -@@ -68,7 +68,13 @@ def wrap_exceptions(fun): - @unittest.skipUnless(WINDOWS, "WINDOWS only") - class TestSystemAPIs(unittest.TestCase): - -+ # Note: Implemented as a staticmethod for ease of sharing with test_posix -+ # for running this test on Cygwin - def test_nic_names(self): -+ return self._test_nic_names(self) -+ -+ @staticmethod -+ def _test_nic_names(test_case): - p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE) - out = p.communicate()[0] - if PY3: -@@ -78,7 +84,7 @@ class TestSystemAPIs(unittest.TestCase): - if "pseudo-interface" in nic.replace(' ', '-').lower(): - continue - if nic not in out: -- self.fail( -+ test_case.fail( - "%r nic wasn't found in 'ipconfig /all' output" % nic) - - @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, -diff --git a/scripts/disk_usage.py b/scripts/disk_usage.py -index 37f4da0..a0924ce 100755 ---- a/scripts/disk_usage.py -+++ b/scripts/disk_usage.py -@@ -48,7 +48,12 @@ def main(): - # ENOENT, pop-up a Windows GUI error for a non-ready - # partition or just hang. - continue -+ if not os.path.exists(part.mountpoint): -+ # In case the mount point itself has been deleted -+ continue -+ - usage = psutil.disk_usage(part.mountpoint) -+ - print(templ % ( - part.device, - bytes2human(usage.total), -diff --git a/scripts/procsmem.py b/scripts/procsmem.py -index a28794b..287a800 100755 ---- a/scripts/procsmem.py -+++ b/scripts/procsmem.py -@@ -41,7 +41,7 @@ import sys - import psutil - - --if not (psutil.LINUX or psutil.OSX or psutil.WINDOWS): -+if not (psutil.LINUX or psutil.OSX or psutil.WINDOWS or psutil.CYGWIN): - sys.exit("platform not supported") - - -diff --git a/setup.py b/setup.py -index 47772da..bd6b5ae 100755 ---- a/setup.py -+++ b/setup.py -@@ -26,6 +26,7 @@ HERE = os.path.abspath(os.path.dirname(__file__)) - sys.path.insert(0, os.path.join(HERE, "psutil")) - - from _common import BSD # NOQA -+from _common import CYGWIN # NOQA - from _common import FREEBSD # NOQA - from _common import LINUX # NOQA - from _common import NETBSD # NOQA -@@ -230,7 +231,33 @@ elif SUNOS: - sources=['psutil/_psutil_sunos.c'], - define_macros=macros, - libraries=['kstat', 'nsl', 'socket']) -+elif CYGWIN: -+ macros.extend([ -+ ("PSUTIL_CYGWIN", 1), -+ ("PSAPI_VERSION", 1) -+ ]) -+ -+ # sys.getwindowsversion() is not available in Cygwin's Python -+ import re -+ winver_re = re.compile(r'CYGWIN_NT-(?P\d+)\.(?P\d+)') - -+ def get_winver(): -+ verstr = os.uname()[0] -+ m = winver_re.search(verstr) -+ maj = int(m.group('major')) -+ min = int(m.group('minor')) -+ return '0x0%s' % ((maj * 100) + min) -+ -+ macros.append(("_WIN32_WINNT", get_winver())) -+ -+ ext = Extension( -+ 'psutil._psutil_cygwin', -+ sources=['psutil/_psutil_cygwin.c', -+ 'psutil/_psutil_common.c', -+ 'psutil/arch/windows/py_error.c', -+ 'psutil/arch/windows/process_info.c'], -+ define_macros=macros, -+ libraries=["psapi", "iphlpapi"]) - else: - sys.exit('platform %s is not supported' % sys.platform) - diff --git a/build/pkgs/psutil/spkg-install.in b/build/pkgs/psutil/spkg-install.in deleted file mode 100644 index 79b1568770f..00000000000 --- a/build/pkgs/psutil/spkg-install.in +++ /dev/null @@ -1,7 +0,0 @@ -if [ "$UNAME" = "Darwin" ] && [ $MACOSX_VERSION -ge 16 ]; then - echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." - CC=clang - export CFLAGS="$CFLAGS_NON_NATIVE" -fi - -cd src && sdh_pip_install . diff --git a/build/pkgs/psutil/type b/build/pkgs/psutil/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/psutil/type +++ /dev/null @@ -1 +0,0 @@ -standard diff --git a/src/requirements.txt.m4 b/src/requirements.txt.m4 index b12c71acb45..57dca4227a6 100644 --- a/src/requirements.txt.m4 +++ b/src/requirements.txt.m4 @@ -34,7 +34,6 @@ dnl pynac # after converting to a pip-installable package dnl From Makefile.in: SAGERUNTIME ipython==esyscmd(`printf $(sed "s/[.]p.*//;" ../ipython/package-version.txt)') pexpect==esyscmd(`printf $(sed "s/[.]p.*//;" ../pexpect/package-version.txt)') -psutil==esyscmd(`printf $(sed "s/[.]p.*//;" ../psutil/package-version.txt)') dnl From Makefile.in: DOC_DEPENDENCIES sphinx==esyscmd(`printf $(sed "s/[.]p.*//;" ../sphinx/package-version.txt)') diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 60ff8b757a6..62825fc8a5b 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -48,7 +48,6 @@ dnl From Makefile.in: SAGERUNTIME esyscmd(`sage-get-system-packages install-requires \ ipython \ pexpect \ - psutil \ | sed "2,\$s/^/ /;"')dnl dnl From Makefile.in: DOC_DEPENDENCIES esyscmd(`sage-get-system-packages install-requires \ From 15884f325b3d4c9d62035e470e67093d7d42575d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 28 Sep 2021 21:36:48 -0700 Subject: [PATCH 031/122] src/sage/matrix/matrix_space.py: Import element classes on demand, fall back to generic on ImportError --- src/sage/matrix/matrix_space.py | 157 ++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 49 deletions(-) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index eabba820926..2dfb022ff32 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -40,33 +40,16 @@ from . import matrix_generic_dense from . import matrix_generic_sparse -from . import matrix_modn_sparse - -from . import matrix_mod2_dense -from . import matrix_gf2e_dense - -from . import matrix_integer_dense -from . import matrix_integer_sparse - -from . import matrix_rational_dense -from . import matrix_rational_sparse - -from . import matrix_polynomial_dense -from . import matrix_mpolynomial_dense - # Sage imports import sage.structure.coerce from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation import sage.rings.integer as integer -import sage.rings.number_field.all -import sage.rings.finite_rings.integer_mod_ring import sage.rings.finite_rings.finite_field_constructor -import sage.rings.polynomial.multi_polynomial_ring_base import sage.misc.latex as latex import sage.modules.free_module -from sage.misc.all import lazy_attribute +from sage.misc.lazy_attribute import lazy_attribute from sage.categories.rings import Rings from sage.categories.fields import Fields @@ -209,25 +192,54 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): if implementation is None: # Choose default implementation: if R is sage.rings.integer_ring.ZZ: - return matrix_integer_dense.Matrix_integer_dense + try: + from . import matrix_integer_dense + except ImportError: + pass + else: + return matrix_integer_dense.Matrix_integer_dense - if R is sage.rings.rational_field.QQ: - return matrix_rational_dense.Matrix_rational_dense + elif R is sage.rings.rational_field.QQ: + try: + from . import matrix_rational_dense + except ImportError: + pass + else: + return matrix_rational_dense.Matrix_rational_dense - if R is sage.rings.real_double.RDF: - from . import matrix_real_double_dense - return matrix_real_double_dense.Matrix_real_double_dense + elif isinstance(R, sage.rings.abc.RealDoubleField): + try: + from . import matrix_real_double_dense + except ImportError: + pass + else: + return matrix_real_double_dense.Matrix_real_double_dense - if R is sage.rings.complex_double.CDF: + elif isinstance(R, sage.rings.abc.ComplexDoubleField): if implementation is None or implementation == 'numpy': - from . import matrix_complex_double_dense - return matrix_complex_double_dense.Matrix_complex_double_dense + try: + from . import matrix_complex_double_dense + except ImportError: + pass + else: + return matrix_complex_double_dense.Matrix_complex_double_dense - if sage.rings.finite_rings.finite_field_constructor.is_FiniteField(R): + elif sage.rings.finite_rings.finite_field_constructor.is_FiniteField(R): if R.order() == 2: - return matrix_mod2_dense.Matrix_mod2_dense + try: + from . import matrix_mod2_dense + except ImportError: + pass + else: + return matrix_mod2_dense.Matrix_mod2_dense + if R.characteristic() == 2 and R.order() <= 65536: # 65536 == 2^16 - return matrix_gf2e_dense.Matrix_gf2e_dense + try: + from . import matrix_gf2e_dense + except ImportError: + pass + else: + return matrix_gf2e_dense.Matrix_gf2e_dense if (not R.is_prime_field()) and R.order() < 256: try: @@ -247,21 +259,47 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): from . import matrix_cyclo_dense return matrix_cyclo_dense.Matrix_cyclo_dense - from sage.symbolic.ring import SR - if R is SR: - from . import matrix_symbolic_dense - return matrix_symbolic_dense.Matrix_symbolic_dense + try: + from sage.symbolic.ring import SR + except ImportError: + pass + else: + if R is SR: + try: + from . import matrix_symbolic_dense + except ImportError: + pass + else: + return matrix_symbolic_dense.Matrix_symbolic_dense - from sage.rings.complex_arb import ComplexBallField - if isinstance(R, ComplexBallField): - from . import matrix_complex_ball_dense - return matrix_complex_ball_dense.Matrix_complex_ball_dense + if isinstance(R, sage.rings.abc.ComplexBallField): + try: + from . import matrix_complex_ball_dense + except ImportError: + pass + else: + return matrix_complex_ball_dense.Matrix_complex_ball_dense - if sage.rings.polynomial.polynomial_ring.is_PolynomialRing(R) and R.base_ring() in _Fields: - return matrix_polynomial_dense.Matrix_polynomial_dense + try: + from sage.rings.polynomial import polynomial_ring, multi_polynomial_ring_base + except ImportError: + pass + else: + if polynomial_ring.is_PolynomialRing(R) and R.base_ring() in _Fields: + try: + from . import matrix_polynomial_dense + except ImportError: + pass + else: + return matrix_polynomial_dense.Matrix_polynomial_dense - if sage.rings.polynomial.multi_polynomial_ring_base.is_MPolynomialRing(R) and R.base_ring() in _Fields: - return matrix_mpolynomial_dense.Matrix_mpolynomial_dense + elif multi_polynomial_ring_base.is_MPolynomialRing(R) and R.base_ring() in _Fields: + try: + from . import matrix_mpolynomial_dense + except ImportError: + pass + else: + return matrix_mpolynomial_dense.Matrix_mpolynomial_dense # The fallback return matrix_generic_dense.Matrix_generic_dense @@ -269,15 +307,19 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): # Deal with request for a specific implementation if implementation == 'flint': if R is sage.rings.integer_ring.ZZ: + from . import matrix_integer_dense return matrix_integer_dense.Matrix_integer_dense if R is sage.rings.rational_field.QQ: + from . import matrix_rational_dense return matrix_rational_dense.Matrix_rational_dense raise ValueError("'flint' matrices are only available over the integers or the rationals") if implementation == 'm4ri': if R.is_field() and R.characteristic() == 2 and R.order() <= 65536: if R.order() == 2: + from . import matrix_mod2_dense return matrix_mod2_dense.Matrix_mod2_dense + from . import matrix_gf2e_dense return matrix_gf2e_dense.Matrix_gf2e_dense raise ValueError("'m4ri' matrices are only available for fields of characteristic 2 and order <= 65536") @@ -326,14 +368,30 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): if implementation is not None: raise ValueError("cannot choose an implementation for sparse matrices") - if isinstance(R, sage.rings.abc.IntegerModRing) and R.order() < matrix_modn_sparse.MAX_MODULUS: - return matrix_modn_sparse.Matrix_modn_sparse + if isinstance(R, sage.rings.abc.IntegerModRing): + try: + from . import matrix_modn_sparse + except ImportError: + pass + else: + if R.order() < matrix_modn_sparse.MAX_MODULUS: + return matrix_modn_sparse.Matrix_modn_sparse if sage.rings.rational_field.is_RationalField(R): - return matrix_rational_sparse.Matrix_rational_sparse + try: + from . import matrix_rational_sparse + except ImportError: + pass + else: + return matrix_rational_sparse.Matrix_rational_sparse if sage.rings.integer_ring.is_IntegerRing(R): - return matrix_integer_sparse.Matrix_integer_sparse + try: + from . import matrix_integer_sparse + except ImportError: + pass + else: + return matrix_integer_sparse.Matrix_integer_sparse # the fallback return matrix_generic_sparse.Matrix_generic_sparse @@ -2454,8 +2512,8 @@ def test_trivial_matrices_inverse(ring, sparse=True, implementation=None, checkr # Fix unpickling Matrix_modn_dense and Matrix_integer_2x2 -from sage.matrix.matrix_modn_dense_double import Matrix_modn_dense_double -from sage.matrix.matrix_integer_dense import Matrix_integer_dense +lazy_import('sage.matrix.matrix_modn_dense_double', 'Matrix_modn_dense_double') +lazy_import('sage.matrix.matrix_integer_dense', 'Matrix_integer_dense') from sage.misc.persist import register_unpickle_override def _MatrixSpace_ZZ_2x2(): from sage.rings.integer_ring import ZZ @@ -2468,5 +2526,6 @@ def _MatrixSpace_ZZ_2x2(): 'MatrixSpace_ZZ_2x2_class', MatrixSpace) register_unpickle_override('sage.matrix.matrix_integer_2x2', 'MatrixSpace_ZZ_2x2', _MatrixSpace_ZZ_2x2) +lazy_import('sage.matrix.matrix_gf2e_dense', 'unpickle_matrix_gf2e_dense_v0') register_unpickle_override('sage.matrix.matrix_mod2e_dense', - 'unpickle_matrix_mod2e_dense_v0', matrix_gf2e_dense.unpickle_matrix_gf2e_dense_v0) + 'unpickle_matrix_mod2e_dense_v0', unpickle_matrix_gf2e_dense_v0) From ac58286892b17ccac976e24fe67aaff436743941 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 4 Oct 2021 22:27:17 -0700 Subject: [PATCH 032/122] Matrix.gram_schmidt: Use sage.rings.abc --- src/sage/matrix/matrix2.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 74e01aa7241..c89931c018f 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10646,10 +10646,8 @@ cdef class Matrix(Matrix1): sage: mu*G == A True """ - import sage.rings.real_double - import sage.rings.complex_double R = self.base_ring() - if R in [sage.rings.real_double.RDF, sage.rings.complex_double.CDF]: + if instance(R, (sage.rings.abc.RealDoubleField, sage.rings.abc.ComplexDoubleField)): Q, R = self.transpose().QR() m = R.nrows(); n = R.ncols() if m > n: From 6d4ee6e7e645fcee216113f8cf86de7e6cc5e833 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 4 Oct 2021 22:30:16 -0700 Subject: [PATCH 033/122] Matrix.gram_schmidt: Use sage.rings.abc (fixup) --- src/sage/matrix/matrix2.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index c89931c018f..d9bf52e7bb4 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10647,7 +10647,7 @@ cdef class Matrix(Matrix1): True """ R = self.base_ring() - if instance(R, (sage.rings.abc.RealDoubleField, sage.rings.abc.ComplexDoubleField)): + if isinstance(R, (sage.rings.abc.RealDoubleField, sage.rings.abc.ComplexDoubleField)): Q, R = self.transpose().QR() m = R.nrows(); n = R.ncols() if m > n: From 056558f11e6f8d141ecfd05896e9f45763d8675d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 8 Oct 2021 15:57:07 -0400 Subject: [PATCH 034/122] Trac #32656: shift argc/argv down during GAP initialization. There used to be between 14 and 18 arguments passed to GAP. Now there are between 10 and 14. Tweak argc/argv appropriately. --- src/sage/libs/gap/util.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index c501a26140e..08c4c40cc01 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -239,7 +239,7 @@ cdef initialize(): # Define argv variable, which we will pass in to # initialize GAP. - cdef char* argv[18] + cdef char* argv[14] argv[0] = "sage" argv[1] = "-l" s = str_to_bytes(gap_root(), FS_ENCODING, "surrogateescape") @@ -256,7 +256,7 @@ cdef initialize(): # 4096 unfortunately is the hard-coded max, but should # be long enough for most cases - cdef int argc = 14 # argv[argc] must be NULL + cdef int argc = 10 # argv[argc] must be NULL from .saved_workspace import workspace workspace, workspace_is_up_to_date = workspace() From 2d852ad84c878ec529300db0e2825f1460b2e0bc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Oct 2021 21:25:04 -0700 Subject: [PATCH 035/122] build/bin/sage-dist-helpers (sdh_pip_uninstall): Use build/bin/sage-pip-uninstall, resurrected in simplified form --- build/bin/sage-dist-helpers | 6 +----- build/bin/sage-pip-uninstall | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100755 build/bin/sage-pip-uninstall diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index 4a8862d50f6..993bc9c65b7 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -316,11 +316,7 @@ sdh_store_and_pip_install_wheel() { } sdh_pip_uninstall() { - # --disable-pip-version-check: Don't periodically check PyPI to determine whether a new version of pip is available - # --no-input: Disable prompting for input. - # --yes: Don't ask for confirmation of uninstall deletions - # See sage-pip-install for a discussion of the other flags. - python3 -m pip uninstall --isolated --disable-pip-version-check --yes --no-input "$@" + sage-pip-uninstall "$@" if [ $? -ne 0 ]; then echo "Warning: pip exited with status $?" >&2 fi diff --git a/build/bin/sage-pip-uninstall b/build/bin/sage-pip-uninstall new file mode 100755 index 00000000000..3017627dbbc --- /dev/null +++ b/build/bin/sage-pip-uninstall @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# This command ensures that any previous installations of the same package +# are uninstalled. + +# Note: "sage-pip-uninstall" is meant to be run after $(PYTHON) has +# been installed (either as an spkg or as a venv over a system python3). +# It is then guaranteed by sage-env that PATH is set in a way that "python3" +# refers to the correct python3. +PYTHON=python3 + +# The PIP variable is only used to determine the name of the lock file. +PIP=pip3 + +# We should avoid running pip while uninstalling a package because that +# is prone to race conditions. Therefore, we use a lockfile while +# running pip. This is implemented in the Python script sage-flock +LOCK="$SAGE_LOCAL/var/lock/$PIP.lock" + + # --disable-pip-version-check: Don't periodically check PyPI to determine whether a new version of pip is available + # --no-input: Disable prompting for input. + # --yes: Don't ask for confirmation of uninstall deletions + # See sage-pip-install for a discussion of the other flags. +sage-flock -x $LOCK $PYTHON -m pip uninstall --isolated --disable-pip-version-check --yes --no-input "$@" From b5a89ad82614300cef196fb0d4eed4a07a591235 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Oct 2021 21:26:00 -0700 Subject: [PATCH 036/122] build/bin/sage-dist-helpers (sdh_store_and_pip_install_wheel): Uninstall before installing, to ensure the wheel is installed even if the version is the same --- build/bin/sage-dist-helpers | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index 993bc9c65b7..a9639b26871 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -306,6 +306,17 @@ sdh_store_and_pip_install_wheel() { local sudo="" local root="" fi + # Trac #32659: pip no longer reinstalls local wheels if the version is the same. + # Because neither (1) applying patches nor (2) local changes (in the case + # of sage-conf, sage-setup, etc.) bump the version number, we need to + # override this behavior. The pip install option --force-reinstall does too + # much -- it also reinstalls all dependencies, which we do not want. + wheel_basename="${wheel##*/}" + distname="${wheel_basename%%-*}" + $sudo python3 -m pip uninstall --isolated --disable-pip-version-check --yes --no-input $distname + if [ $? -ne 0 ]; then + echo "(ignoring error)" >&2 + fi $sudo sage-pip-install $root $pip_options "$wheel" || \ sdh_die "Error installing ${wheel##*/}" if [ -n "${SAGE_PKG_DIR}" ]; then From 30a272e613ecf63b12702bc7fa1d630f2502ce83 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 9 Oct 2021 21:26:49 -0700 Subject: [PATCH 037/122] build/pkgs/sage_setup/dependencies: Add interpreter specs as dependencies --- build/pkgs/sage_setup/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sage_setup/dependencies b/build/pkgs/sage_setup/dependencies index 99553f0cc9b..d99d4aed55e 100644 --- a/build/pkgs/sage_setup/dependencies +++ b/build/pkgs/sage_setup/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython pkgconfig | $(PYTHON_TOOLCHAIN) +$(PYTHON) cython pkgconfig $(SAGE_ROOT)/pkgs/sage-setup/sage_setup/autogen/interpreters/specs/*.py | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From bfe5108094e6dc2e6b0dc9ad2e6d9dce76d206f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 13 Oct 2021 08:36:47 +0200 Subject: [PATCH 038/122] fix pycodestyle warnings again --- src/sage/combinat/posets/posets.py | 2 +- src/sage/combinat/words/morphic.py | 4 ++-- src/sage/tests/finite_poset.py | 8 ++++---- src/tox.ini | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 3c5b56dedf2..32b64020004 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -5693,7 +5693,7 @@ def lexicographic_sum(self, P): True """ # P might be defaultdict, hence the test - if type(P) == type({}): + if isinstance(P, dict): if set(P) != set(self): raise ValueError("keys of dict P does not match to elements of the poset") diff --git a/src/sage/combinat/words/morphic.py b/src/sage/combinat/words/morphic.py index 3311872c50c..b6085868ee9 100644 --- a/src/sage/combinat/words/morphic.py +++ b/src/sage/combinat/words/morphic.py @@ -124,8 +124,8 @@ def __init__(self, parent, morphism, letter, coding=None, length=Infinity): self._morphism = morphism self._letter = letter self._alphabet = self._morphism.domain().alphabet() - if coding == None: - self._coding = {a:a for a in self._alphabet} + if coding is None: + self._coding = {a: a for a in self._alphabet} else: self._coding = coding diff --git a/src/sage/tests/finite_poset.py b/src/sage/tests/finite_poset.py index c4112cf9f95..56495b04492 100644 --- a/src/sage/tests/finite_poset.py +++ b/src/sage/tests/finite_poset.py @@ -209,7 +209,7 @@ def test_finite_lattice(L): p = "is_"+p_ if 'certificate' in sage_getargspec(getattr(L, p)).args: res = attrcall(p, certificate=True)(L) - if type(res) != type((1,2)) or len(res) != 2: + if not isinstance(res, tuple) or len(res) != 2: raise ValueError("certificate-option does not return a pair in %s" % p) if P[p_] != res[0]: raise ValueError("certificate-option changes result in %s" % p) @@ -609,11 +609,11 @@ def test_finite_poset(P): 'jump_critical', 'meet_semilattice', 'slender'] for p in bool_with_cert: try: # some properties are not always defined for all posets - res1 = attrcall('is_'+p)(P) + res1 = attrcall('is_' + p)(P) except ValueError: continue - res2 = attrcall('is_'+p, certificate=True)(P) - if type(res2) != type((1,2)) or len(res2) != 2: + res2 = attrcall('is_' + p, certificate=True)(P) + if not isinstance(res2, tuple) or len(res2) != 2: raise ValueError("certificate-option does not return a pair in %s" % p) if res1 != res2[0]: raise ValueError("certificate-option changes result in %s" % p) diff --git a/src/tox.ini b/src/tox.ini index 668483ce6a4..08abc70e5f6 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -83,9 +83,10 @@ description = # W605: invalid escape sequence ‘x’ # E711: comparison to None should be ‘if cond is None:’ # E712: comparison to True should be ‘if cond is True:’ or ‘if cond:’ + # E721: do not compare types, use isinstance() # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E401,E70,W605,E711,E712 {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E401,E70,W605,E711,E712,E721 {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 From 119f105d5fe385f11f55c7e632006e9fe49d4806 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Wed, 13 Oct 2021 22:25:02 +0200 Subject: [PATCH 039/122] Trac 32685: Fix y label in set_axes_labels --- src/sage/manifolds/utilities.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/manifolds/utilities.py b/src/sage/manifolds/utilities.py index ae729e33206..a95c9e8d13d 100644 --- a/src/sage/manifolds/utilities.py +++ b/src/sage/manifolds/utilities.py @@ -1270,11 +1270,10 @@ def set_axes_labels(graph, xlabel, ylabel, zlabel, **kwds): y1 = ymin + dy / 2 z1 = zmin + dz / 2 xmin1 = xmin - dx / 20 - xmax1 = xmax + dx / 20 ymin1 = ymin - dy / 20 zmin1 = zmin - dz / 20 graph += text3d(' ' + xlabel, (x1, ymin1, zmin1), **kwds) - graph += text3d(' ' + ylabel, (xmax1, y1, zmin1), **kwds) + graph += text3d(' ' + ylabel, (xmin1, y1, zmin1), **kwds) graph += text3d(' ' + zlabel, (xmin1, ymin1, z1), **kwds) return graph From 565546641bf8690e5a3da04f9d0d7819d2de50e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 14 Oct 2021 09:49:22 +0200 Subject: [PATCH 040/122] add factor method to Tamari Interval Poset --- src/sage/combinat/interval_posets.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index ed2de3bd9ce..98abeaa6427 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -597,6 +597,34 @@ def _mul_(self, other: TIP) -> TIP: format='vertices_and_edges')) return TamariIntervalPoset(P, check=False) # type:ignore + def factor(self) -> list[TamariIntervalPoset]: + """ + Return the decomposition as a list of connected components. + + EXAMPLES:: + + sage: factor(TamariIntervalPoset(2,[])) # indirect doctest + [The Tamari interval of size 1 induced by relations [], + The Tamari interval of size 1 induced by relations []] + + .. SEEALSO:: :meth:`is_connected` + + TESTS:: + + sage: T = TamariIntervalPosets(20).random_element() + sage: T == prod(factor(T)) + True + """ + hasse = self.poset().hasse_diagram() + cc = hasse.connected_components_subgraphs() + resu = [] + for comp in sorted(cc, key=min): + shift = 1 - min(comp) + comp.relabel(lambda i: i + shift) + resu.append(TamariIntervalPoset(len(comp), + comp.edges(labels=False))) + return resu + def __hash__(self): """ Return the hash of ``self``. From f630951d2366b48e073e2fa57cc7f2e398cf88e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 13 Oct 2021 20:16:04 +0200 Subject: [PATCH 041/122] various details in combinat and functions --- .../combinat/crystals/elementary_crystals.py | 60 ++++++++++--------- src/sage/combinat/words/abstract_word.py | 27 ++++----- src/sage/combinat/words/suffix_trees.py | 13 ++-- src/sage/functions/generalized.py | 11 ++-- src/sage/functions/jacobi.py | 13 ++-- src/sage/manifolds/subsets/pullback.py | 3 +- 6 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/sage/combinat/crystals/elementary_crystals.py b/src/sage/combinat/crystals/elementary_crystals.py index 83f1f1fd86a..632623f39c4 100644 --- a/src/sage/combinat/crystals/elementary_crystals.py +++ b/src/sage/combinat/crystals/elementary_crystals.py @@ -53,7 +53,7 @@ - [NZ1997]_ """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013 Ben Salisbury # # Distributed under the terms of the GNU General Public License (GPL) @@ -65,8 +65,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#**************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.categories.crystals import Crystals from sage.categories.finite_crystals import FiniteCrystals @@ -88,7 +88,7 @@ class AbstractSingleCrystalElement(Element): r""" Abstract base class for elements in crystals with a single element. """ - def __lt__(self,other): + def __lt__(self, other): r""" EXAMPLES:: @@ -113,7 +113,7 @@ def __hash__(self): """ return hash(self.parent()) - def __eq__(self,other): + def __eq__(self, other): r""" EXAMPLES:: @@ -141,7 +141,7 @@ def __eq__(self,other): return self.parent() is other.parent() return False - def __ne__(self,other): + def __ne__(self, other): r""" EXAMPLES:: @@ -154,7 +154,7 @@ def __ne__(self,other): """ return not self == other - def e(self,i): + def e(self, i): r""" Return `e_i` of ``self``, which is ``None`` for all `i`. @@ -173,7 +173,7 @@ def e(self,i): """ return None - def f(self,i): + def f(self, i): r""" Return `f_i` of ``self``, which is ``None`` for all `i`. @@ -192,6 +192,7 @@ def f(self,i): """ return None + class TCrystal(UniqueRepresentation, Parent): r""" The crystal `T_{\lambda}`. @@ -289,7 +290,7 @@ def _repr_(self): sage: B The T crystal of type ['E', 6] and weight Lambda[6] """ - return "The T crystal of type {1!s} and weight {0!s}".format(self._weight,self._cartan_type) + return "The T crystal of type {1!s} and weight {0!s}".format(self._weight, self._cartan_type) def _element_constructor_(self, weight): r""" @@ -307,7 +308,7 @@ def _element_constructor_(self, weight): Lambda[7] + Lambda[8] """ if weight != self._weight: - raise ValueError("Only element is t(%s)"%self._weight) + raise ValueError("Only element is t(%s)" % self._weight) return self.element_class(self) def cardinality(self): @@ -375,9 +376,9 @@ def _latex_(self): sage: latex(t) {t_{-e_{0} - 3e_{1} - 3e_{2} - 3e_{deltacheck}}} """ - return "{t_{"+self.parent()._weight._latex_()+"}}" + return "{t_{" + self.parent()._weight._latex_() + "}}" - def epsilon(self,i): + def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``, which is `-\infty` for all `i`. @@ -396,7 +397,7 @@ def epsilon(self,i): """ return float("-inf") - def phi(self,i): + def phi(self, i): r""" Return `\varphi_i` of ``self``, which is `-\infty` for all `i`. @@ -430,6 +431,7 @@ def weight(self): """ return self.parent()._weight + class RCrystal(UniqueRepresentation, Parent): r""" The crystal `R_{\lambda}`. @@ -568,7 +570,7 @@ def _element_constructor_(self, weight): Lambda[7] + Lambda[8] """ if weight != self._weight: - raise ValueError("Only element is r(%s)"%self._weight) + raise ValueError("Only element is r(%s)" % self._weight) return self.element_class(self) def cardinality(self): @@ -640,8 +642,8 @@ def _latex_(self): {r^{\vee}_{\Lambda_{1}}} """ if self.parent()._dual: - return r"{r^{\vee}_{"+self.parent()._weight._latex_()+"}}" - return "{r_{"+self.parent()._weight._latex_()+"}}" + return r"{r^{\vee}_{" + self.parent()._weight._latex_() + "}}" + return "{r_{" + self.parent()._weight._latex_() + "}}" def epsilon(self, i): r""" @@ -798,10 +800,10 @@ def __init__(self, cartan_type, i): sage: B = crystals.elementary.Elementary("D4",3) sage: TestSuite(B).run() """ - Parent.__init__(self, category = (Crystals(), InfiniteEnumeratedSets())) + Parent.__init__(self, category=(Crystals(), InfiniteEnumeratedSets())) self._i = i self._cartan_type = cartan_type - self.module_generators = (self.element_class(self,0),) + self.module_generators = (self.element_class(self, 0),) def _repr_(self): r""" @@ -813,7 +815,7 @@ def _repr_(self): sage: B The 4-elementary crystal of type ['B', 5, 1] """ - return "The {0!s}-elementary crystal of type {1!s}".format(self._i,self._cartan_type) + return "The {0!s}-elementary crystal of type {1!s}".format(self._i, self._cartan_type) def _element_constructor_(self, m): r""" @@ -883,7 +885,7 @@ def _repr_(self): """ return repr(self._m) - def __lt__(self,other): + def __lt__(self, other): r""" EXAMPLES:: @@ -899,7 +901,7 @@ def __lt__(self,other): return False return Integer(self._m) < Integer(other._m) - def __eq__(self,other): + def __eq__(self, other): r""" EXAMPLES:: @@ -915,7 +917,7 @@ def __eq__(self,other): return self.parent() is other.parent() and self._m == other._m return False - def __ne__(self,other): + def __ne__(self, other): r""" EXAMPLES:: @@ -937,9 +939,9 @@ def _latex_(self): sage: latex(B(26)) {b_{6}(26)} """ - return "{b_{%s}(%s)}"%(self.parent()._i, self._m) + return "{b_{%s}(%s)}" % (self.parent()._i, self._m) - def e(self,i): + def e(self, i): r""" Return the action of `e_i` on ``self``. @@ -957,7 +959,7 @@ def e(self,i): sage: B(0).e(2) """ if i == self.parent()._i: - return self.__class__(self.parent(), self._m+1) + return self.__class__(self.parent(), self._m + 1) else: return None @@ -979,7 +981,7 @@ def f(self, i): sage: B(0).e(2) """ if i == self.parent()._i: - return self.__class__(self.parent(), self._m-1) + return self.__class__(self.parent(), self._m - 1) else: return None @@ -1042,6 +1044,7 @@ def weight(self): Q = self.parent().weight_lattice_realization() return self._m * Q.simple_root(self.parent()._i) + class ComponentCrystal(UniqueRepresentation, Parent): r""" The component crystal. @@ -1193,7 +1196,7 @@ def _latex_(self): """ return "{c}" - def epsilon(self,i): + def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``, which is `0` for all `i`. @@ -1210,7 +1213,7 @@ def epsilon(self,i): """ return 0 - def phi(self,i): + def phi(self, i): r""" Return `\varphi_i` of ``self``, which is `0` for all `i`. @@ -1239,4 +1242,3 @@ def weight(self): (0, 0, 0, 0) """ return self.parent().weight_lattice_realization().zero() - diff --git a/src/sage/combinat/words/abstract_word.py b/src/sage/combinat/words/abstract_word.py index fc0b731ac12..33261cad6f2 100644 --- a/src/sage/combinat/words/abstract_word.py +++ b/src/sage/combinat/words/abstract_word.py @@ -6,7 +6,7 @@ AUTHORS: -- Sebastien Labbe +- Sébastien Labbé - Franco Saliola EXAMPLES:: @@ -20,7 +20,7 @@ sage: p.length() 231 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2010 Sebastien Labbe , # 2008-2010 Franco Saliola # @@ -28,8 +28,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 sage.structure.sage_object import SageObject from sage.combinat.words.word_options import word_options @@ -127,7 +127,7 @@ def string_rep(self): if word_options['display'] == 'string': ls = word_options['letter_separator'] letters = [str(_) for _ in letters] - if all(len(a)==1 for a in letters): + if all(len(a) == 1 for a in letters): return ''.join(letters) + suffix elif suffix == "...": return ls.join(letters) + ls + suffix @@ -349,7 +349,7 @@ def __richcmp__(self, other, op): cs = next(self_it) except StopIteration: try: - co = next(other_it) + next(other_it) except StopIteration: # If both self_it and other_it are exhausted then # self == other. Return 0. @@ -631,7 +631,7 @@ def _to_integer_iterator(self, use_parent_alphabet=False): """ from sage.combinat.words.words import FiniteWords, InfiniteWords if use_parent_alphabet and\ - isinstance(self.parent(), (FiniteWords,InfiniteWords)): + isinstance(self.parent(), (FiniteWords, InfiniteWords)): A = self.parent().alphabet() for letter in self: yield A.rank(letter) @@ -1161,8 +1161,8 @@ def prefixes_iterator(self, max_length=None): """ to_consider = self if max_length is None else self[:max_length] yield self[:0] - for (i,a) in enumerate(to_consider): - yield self[:i+1] + for (i, a) in enumerate(to_consider): + yield self[:i + 1] def palindrome_prefixes_iterator(self, max_length=None): r""" @@ -1244,7 +1244,7 @@ def _partial_sums_iterator(self, start, mod=None): sum = Zn(start) else: - raise TypeError('mod(=%s) must be None or an integer'%mod) + raise TypeError('mod(=%s) must be None or an integer' % mod) yield sum for letter in self: @@ -1385,7 +1385,7 @@ def _finite_differences_iterator(self, mod=None): return else: - raise TypeError('mod(=%s) must be None or an integer'%mod) + raise TypeError('mod(=%s) must be None or an integer' % mod) def finite_differences(self, mod=None): r""" @@ -1529,7 +1529,7 @@ def sum_digits(self, base=2, mod=None): elif mod in ZZ and mod >= 2: alphabet = list(range(mod)) else: - raise ValueError("base (=%s) and mod (=%s) must be integers greater or equal to 2"%(base, mod)) + raise ValueError("base (=%s) and mod (=%s) must be integers greater or equal to 2" % (base, mod)) # The iterator f = partial(words._ThueMorseWord_nth_digit, alphabet=alphabet, base=base) @@ -1609,7 +1609,7 @@ def first_occurrence(self, other, start=0): for j in range(lf-1, -1, -1): a = self[s+j] if other[j] != a: - s += max(suff[j + 1], j - occ.get(a,-1)) + s += max(suff[j + 1], j - occ.get(a, -1)) break else: return s @@ -1754,4 +1754,3 @@ def complete_return_words_iterator(self, fact): i = j except StopIteration: return - diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 3eebe3efe08..de5761e2901 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -1186,34 +1186,33 @@ def number_of_factors(self,n=None): """ if n is None: length_word = self.word().length() - num_factors = 1 # empty word - for (u,v,(i,j)) in self.edge_iterator(): + num_factors = 1 # empty word + for (u, v, (i, j)) in self.edge_iterator(): if j is None: num_factors += length_word - i else: num_factors += j - i elif isinstance(n, (int, Integer)): - length_word = self.word().length() num_factors = 0 queue = [(0, 0)] while queue: - (v,l) = queue.pop() + (v, l) = queue.pop() if l == n: num_factors += 1 if l < n: if self._transition_function[v] != {}: - for ((i,j),u) in self._transition_function[v].items(): + for ((i, j), u) in self._transition_function[v].items(): if j is None: j = self.word().length() if j - i >= n - l: num_factors += 1 else: - queue.append((u,l+j-i+1)) + queue.append((u, l + j - i + 1)) else: raise TypeError("not an integer or None: %s" % n) return num_factors - def factor_iterator(self,n=None): + def factor_iterator(self, n=None): r""" Generate distinct factors of ``self``. diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index 6c38ef5621c..8c3f3fcf9b9 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -592,7 +592,7 @@ def _eval_(self, m, n): Kronecker delta is a symmetric function. We keep arguments sorted to ensure that k_d(m, n) - k_d(n, m) cancels automatically:: - sage: x,y=var('x,y') + sage: x,y = var('x,y') sage: kronecker_delta(x, y) kronecker_delta(x, y) sage: kronecker_delta(y, x) @@ -621,17 +621,14 @@ def _evalf_(self, m, n, **kwds): """ if bool(repr(m) > repr(n)): return kronecker_delta(n, m) - x = m - n approx_x = ComplexIntervalField()(x) - if bool(approx_x.imag() == 0): # x is real - if bool(approx_x.real() == 0): # x is zero + if approx_x.imag() == 0: # x is real + if approx_x.real() == 0: # x is zero return 1 else: return 0 - else: - return 0 # x is complex - raise ValueError("Numeric evaluation of symbolic expression") + return 0 # x is complex def _derivative_(self, *args, **kwds): """ diff --git a/src/sage/functions/jacobi.py b/src/sage/functions/jacobi.py index b61892dda09..39db1ed8efb 100644 --- a/src/sage/functions/jacobi.py +++ b/src/sage/functions/jacobi.py @@ -136,17 +136,15 @@ - Eviatar Bach (2013): complete rewrite, new numerical evaluation, and addition of the Jacobi amplitude function """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 David Joyner # Copyright (C) 2013 Eviatar Bach # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.symbolic.function import BuiltinFunction from sage.functions.trig import (arctan, arcsin, arccos, arccot, arcsec, arccsc, csc, sec, sin, cos, tan, cot) @@ -977,6 +975,7 @@ def jacobi(kind, z, m, **kwargs): raise ValueError("kind must be one of 'nd', 'ns', 'nc', 'dn', " "'ds', 'dc', 'sn', 'sd', 'sc', 'cn', 'cd', 'cs'.") + def inverse_jacobi(kind, x, m, **kwargs): r""" The inverses of the 12 Jacobi elliptic functions. They have the property @@ -1135,6 +1134,7 @@ def _print_latex_(self, x, m): return r"\operatorname{{am}}\left({}\middle|{}\right)".format(latex(x), latex(m)) + jacobi_am = JacobiAmplitude() @@ -1293,7 +1293,7 @@ def inverse_jacobi_f(kind, x, m): ctx.prec += 10 phi = ctx.asin(x) return sign * ctx.ellipf(phi, m) - elif 1 < x <= 1 / ctx.sqrt(m): + elif x <= 1 / ctx.sqrt(m): K = ctx.ellipk(m) ctx.prec += 10 xpn2 = x ** (-2) @@ -1682,4 +1682,3 @@ def jacobi_am_f(x, m): return ctx.atan2(snz, cnz) + npi finally: ctx.prec = prec - diff --git a/src/sage/manifolds/subsets/pullback.py b/src/sage/manifolds/subsets/pullback.py index 8453e346b29..0205222fd2a 100644 --- a/src/sage/manifolds/subsets/pullback.py +++ b/src/sage/manifolds/subsets/pullback.py @@ -15,7 +15,7 @@ from sage.categories.sets_cat import Sets, EmptySetError from sage.categories.metric_spaces import MetricSpaces -from sage.modules.free_module import is_FreeModule, FreeModule +from sage.modules.free_module import is_FreeModule from sage.rings.infinity import infinity, minus_infinity from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -573,6 +573,7 @@ def __init__(self, map, codomain_subset, inverse, name, latex_name): if chart.domain().dimension() != 1: raise ValueError('to pull back a set of scalars by a chart, the manifold must be 1-dimensional') map = chart.domain().scalar_field({chart: chart[0]}) + def _inverse(coord): return self.point((coord,), chart=chart) else: From 09aabf429b7c8e5c132368e1b5deb97132fce75b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 14 Oct 2021 12:29:54 -0400 Subject: [PATCH 042/122] Trac #32691: remove obsolete easy_install lockfile patch. The easy_install mechanism is obsolete, and was replaced by pip. None of our SPKGs require it, so the patch to allow simultaneous installs is also obsolete. We remove it. --- build/pkgs/setuptools/SPKG.rst | 2 -- .../patches/easy_install_lock.patch | 31 ------------------- 2 files changed, 33 deletions(-) delete mode 100644 build/pkgs/setuptools/patches/easy_install_lock.patch diff --git a/build/pkgs/setuptools/SPKG.rst b/build/pkgs/setuptools/SPKG.rst index 0b2d042f946..10e732330c6 100644 --- a/build/pkgs/setuptools/SPKG.rst +++ b/build/pkgs/setuptools/SPKG.rst @@ -37,5 +37,3 @@ applied during the build process. - pkg_resources.py.patch: silence warning about permissions. -- easy_install_lock.patch: lock the easy_install.pth file to allow - simultaneous installation diff --git a/build/pkgs/setuptools/patches/easy_install_lock.patch b/build/pkgs/setuptools/patches/easy_install_lock.patch deleted file mode 100644 index 0acc44aff37..00000000000 --- a/build/pkgs/setuptools/patches/easy_install_lock.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff -ru src/setuptools/command/easy_install.py b/setuptools/command/easy_install.py ---- src/setuptools/command/easy_install.py 2014-10-31 14:42:35.779973945 +0100 -+++ b/setuptools/command/easy_install.py 2014-10-31 15:07:53.067034724 +0100 -@@ -1446,6 +1446,11 @@ - list(map(self.add, find_distributions(path, True))) - - def _load(self): -+ # Lock self.filename until save() is called -+ import fcntl -+ self.lock = open(self.filename, 'a') -+ fcntl.flock(self.lock, fcntl.LOCK_EX) -+ - self.paths = [] - saw_import = False - seen = dict.fromkeys(self.sitedirs) -@@ -1479,6 +1484,7 @@ - def save(self): - """Write changed .pth file back to disk""" - if not self.dirty: -+ self.lock.close() - return - - data = '\n'.join(map(self.make_relative,self.paths)) -@@ -1504,6 +1510,7 @@ - os.unlink(self.filename) - - self.dirty = False -+ self.lock.close() - - def add(self, dist): - """Add `dist` to the distribution map""" From 4376f03e0dbdf4ff238535c100fe1f311449e8b7 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 14 Oct 2021 12:31:55 -0400 Subject: [PATCH 043/122] Trac #32691: remove description of a nonexistent patch in SPKG.rst. --- build/pkgs/setuptools/SPKG.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build/pkgs/setuptools/SPKG.rst b/build/pkgs/setuptools/SPKG.rst index 10e732330c6..8d510960f1d 100644 --- a/build/pkgs/setuptools/SPKG.rst +++ b/build/pkgs/setuptools/SPKG.rst @@ -27,13 +27,3 @@ Dependencies ------------ - python - - -Build Instructions/Changes --------------------------- - -The following patches are in the patches subdirectory. The patches are -applied during the build process. - -- pkg_resources.py.patch: silence warning about permissions. - From cc028a3e0354ee879f0129f41b7828592d522b3f Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 14 Oct 2021 15:19:45 -0400 Subject: [PATCH 044/122] Trac #32691: upgrade setuptools to v58.2.0. --- build/pkgs/setuptools/checksums.ini | 6 +++--- build/pkgs/setuptools/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 854f0a9f02b..6a280871e78 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools-VERSION.tar.gz -sha1=527fb70bfac4a7396d052843a6f2ec70f9f45f68 -md5=f81a78919878159a2fd677591f7b8ac3 -cksum=2062383309 +sha1=5d97de0e774b2269c85685a4aa8fd5956bcfd2bb +md5=ff20ab7e0d51c5ad0a9438c50e598c06 +cksum=3564252314 upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.tar.gz diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 5d46e1226e1..e6ea6360512 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -58.0.2 +58.2.0 From 87d5e6143a93f19d0102420b964449d068ee561e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 15 Oct 2021 09:21:38 +0200 Subject: [PATCH 045/122] fix --- src/sage/combinat/posets/posets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 32b64020004..9ebe89e468e 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -281,7 +281,7 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - +from collections import defaultdict from copy import copy, deepcopy from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute @@ -2011,7 +2011,6 @@ def plot(self, label_elements=True, element_labels=None, sage: P.plot() Graphics object consisting of 0 graphics primitives """ - from collections import defaultdict graph = self.hasse_diagram() rename = {'element_color': 'vertex_color', @@ -5693,7 +5692,7 @@ def lexicographic_sum(self, P): True """ # P might be defaultdict, hence the test - if isinstance(P, dict): + if isinstance(P, dict) and not isinstance(P, defaultdict): if set(P) != set(self): raise ValueError("keys of dict P does not match to elements of the poset") From cf83e77ac6b99b5c012ad3e56f8697dcfe312b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 15 Oct 2021 14:04:36 +0200 Subject: [PATCH 046/122] more doc for factor in interval_posets --- src/sage/combinat/interval_posets.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index 98abeaa6427..b9d16ccc4df 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -599,7 +599,7 @@ def _mul_(self, other: TIP) -> TIP: def factor(self) -> list[TamariIntervalPoset]: """ - Return the decomposition as a list of connected components. + Return the unique decomposition as a list of connected components. EXAMPLES:: @@ -612,7 +612,10 @@ def factor(self) -> list[TamariIntervalPoset]: TESTS:: sage: T = TamariIntervalPosets(20).random_element() - sage: T == prod(factor(T)) + sage: facs = factor(T) + sage: all(U.is_connected() for U in facs) + True + sage: T == prod(facs) True """ hasse = self.poset().hasse_diagram() @@ -2807,7 +2810,7 @@ def is_connected(self) -> bool: This condition is invariant under complementation. - .. SEEALSO:: :meth:`is_indecomposable` + .. SEEALSO:: :meth:`is_indecomposable`, :meth:`factor` EXAMPLES:: From a990a14062bd7ad8ddade5804585804e2db7e34e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 15 Oct 2021 23:33:03 -0700 Subject: [PATCH 047/122] Add sage.rings.abc.AlgebraicField etc., deprecate is_AlgebraicField --- src/sage/modular/dirichlet.py | 5 ++- src/sage/rings/abc.pyx | 24 +++++++++++++ .../rings/polynomial/polynomial_element.pyx | 30 +++++----------- src/sage/rings/qqbar.py | 36 ++++++++++++++++--- src/sage/rings/qqbar_decorators.py | 6 ++-- src/sage/symbolic/expression.pyx | 26 +++++++------- 6 files changed, 81 insertions(+), 46 deletions(-) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 74345c67e5d..c35e712de21 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -69,7 +69,6 @@ from sage.categories.map import Map from sage.rings.rational_field import is_RationalField import sage.rings.abc -from sage.rings.qqbar import is_AlgebraicField from sage.rings.ring import is_Ring from sage.misc.functional import round @@ -1347,7 +1346,7 @@ def gauss_sum(self, a=1): m = G.modulus() if isinstance(K, sage.rings.abc.ComplexField): return self.gauss_sum_numerical(a=a) - elif is_AlgebraicField(K): + elif isinstance(K, sage.rings.abc.AlgebraicField): L = K zeta = L.zeta(m) elif number_field.is_CyclotomicField(K) or is_RationalField(K): @@ -1427,7 +1426,7 @@ def gauss_sum_numerical(self, prec=53, a=1): def phi(t): return t CC = K - elif is_AlgebraicField(K): + elif isinstance(K, sage.rings.abc.AlgebraicField): from sage.rings.complex_mpfr import ComplexField CC = ComplexField(prec) phi = CC.coerce_map_from(K) diff --git a/src/sage/rings/abc.pyx b/src/sage/rings/abc.pyx index 2c5e2253150..15e195a4e1a 100644 --- a/src/sage/rings/abc.pyx +++ b/src/sage/rings/abc.pyx @@ -2,6 +2,30 @@ Abstract base classes for rings """ +class AlgebraicField_common(Field): + r""" + Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField_common`. + """ + + pass + + +class AlgebraicField(AlgebraicField_common): + r""" + Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField`. + """ + + pass + + +class AlgebraicRealField(AlgebraicField_common): + r""" + Abstract base class for :class:`~sage.rings.qqbar.AlgebraicRealField`. + """ + + pass + + cdef class RealField(Field): r""" Abstract base class for :class:`~sage.rings.real_mpfr.RealField_class`. diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 3b944b50ce6..7d47f0dbeb1 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -171,31 +171,17 @@ from .polynomial_compiled cimport CompiledPolynomialFunction from sage.rings.polynomial.polydict cimport ETuple -cdef object is_AlgebraicRealField -cdef object is_AlgebraicField -cdef object is_AlgebraicField_common cdef object NumberField_quadratic -cdef object is_ComplexIntervalField cdef void late_import(): # A hack to avoid circular imports. - global is_AlgebraicRealField - global is_AlgebraicField - global is_AlgebraicField_common global NumberField_quadratic - global is_ComplexIntervalField - if is_AlgebraicRealField is not None: + if NumberField_quadratic is not None: return - import sage.rings.qqbar - is_AlgebraicRealField = sage.rings.qqbar.is_AlgebraicRealField - is_AlgebraicField = sage.rings.qqbar.is_AlgebraicField - is_AlgebraicField_common = sage.rings.qqbar.is_AlgebraicField_common import sage.rings.number_field.number_field NumberField_quadratic = sage.rings.number_field.number_field.NumberField_quadratic - import sage.rings.complex_interval_field - is_ComplexIntervalField = sage.rings.complex_interval_field.is_ComplexIntervalField cdef class Polynomial(CommutativeAlgebraElement): @@ -7964,16 +7950,16 @@ cdef class Polynomial(CommutativeAlgebraElement): else: return [rt.rhs() for rt in rts] - if L != K or is_AlgebraicField_common(L): + if L != K or isinstance(L, sage.rings.abc.AlgebraicField_common): # So far, the only "special" implementations are for real # and complex root isolation and for p-adic factorization if (is_IntegerRing(K) or is_RationalField(K) - or is_AlgebraicRealField(K)) and \ - (is_AlgebraicRealField(L) or isinstance(L, sage.rings.abc.RealIntervalField)): + or isinstance(K, sage.rings.abc.AlgebraicRealField)) and \ + isinstance(L, (sage.rings.abc.AlgebraicRealField, sage.rings.abc.RealIntervalField)): from sage.rings.polynomial.real_roots import real_roots - if is_AlgebraicRealField(L): + if isinstance(L, sage.rings.abc.AlgebraicRealField): rts = real_roots(self, retval='algebraic_real') else: diam = ~(ZZ(1) << L.prec()) @@ -7998,14 +7984,14 @@ cdef class Polynomial(CommutativeAlgebraElement): return [rt for (rt, mult) in rts] if (is_IntegerRing(K) or is_RationalField(K) - or is_AlgebraicField_common(K) or input_gaussian) and \ - (isinstance(L, sage.rings.abc.ComplexIntervalField) or is_AlgebraicField_common(L)): + or isinstance(K, sage.rings.abc.AlgebraicField_common) or input_gaussian) and \ + isinstance(L, (sage.rings.abc.ComplexIntervalField, sage.rings.abc.AlgebraicField_common)): from sage.rings.polynomial.complex_roots import complex_roots if isinstance(L, sage.rings.abc.ComplexIntervalField): rts = complex_roots(self, min_prec=L.prec()) - elif is_AlgebraicField(L): + elif isinstance(L, sage.rings.abc.AlgebraicField): rts = complex_roots(self, retval='algebraic') else: rts = complex_roots(self, retval='algebraic_real') diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 3c3343e5354..3e7b39c896e 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -555,6 +555,7 @@ import operator import sage.rings.ring +import sage.rings.abc import sage.rings.number_field.number_field_base from sage.misc.fast_methods import Singleton from sage.misc.cachefunc import cached_method @@ -583,7 +584,7 @@ CC = ComplexField() CIF = ComplexIntervalField() -class AlgebraicField_common(sage.rings.ring.Field): +class AlgebraicField_common(sage.rings.abc.AlgebraicField_common): r""" Common base class for the classes :class:`~AlgebraicRealField` and :class:`~AlgebraicField`. @@ -1013,7 +1014,7 @@ def NF_elem_map(e): return Factorization(factorization, unit = f.lc() / trial.lc()) -class AlgebraicRealField(Singleton, AlgebraicField_common): +class AlgebraicRealField(Singleton, AlgebraicField_common, sage.rings.abc.AlgebraicRealField): r""" The field of algebraic reals. @@ -1449,12 +1450,21 @@ def is_AlgebraicRealField(F): r""" Check whether ``F`` is an :class:`~AlgebraicRealField` instance. For internal use. + This function is deprecated. Use :func:`isinstance` with + :class:`~sage.rings.abc.AlgebraicRealField` instead. + EXAMPLES:: sage: from sage.rings.qqbar import is_AlgebraicRealField sage: [is_AlgebraicRealField(x) for x in [AA, QQbar, None, 0, "spam"]] + doctest:warning... + DeprecationWarning: is_AlgebraicRealField is deprecated; + use isinstance(..., sage.rings.abc.AlgebraicRealField instead + See https://trac.sagemath.org/32660 for details. [True, False, False, False, False] """ + from sage.misc.superseded import deprecation + deprecation(32660, 'is_AlgebraicRealField is deprecated; use isinstance(..., sage.rings.abc.AlgebraicRealField instead') return isinstance(F, AlgebraicRealField) @@ -1462,7 +1472,7 @@ def is_AlgebraicRealField(F): AA = AlgebraicRealField() -class AlgebraicField(Singleton, AlgebraicField_common): +class AlgebraicField(Singleton, AlgebraicField_common, sage.rings.abc.AlgebraicField): """ The field of all algebraic complex numbers. """ @@ -1993,12 +2003,21 @@ def is_AlgebraicField(F): r""" Check whether ``F`` is an :class:`~AlgebraicField` instance. + This function is deprecated. Use :func:`isinstance` with + :class:`~sage.rings.abc.AlgebraicField` instead. + EXAMPLES:: sage: from sage.rings.qqbar import is_AlgebraicField sage: [is_AlgebraicField(x) for x in [AA, QQbar, None, 0, "spam"]] + doctest:warning... + DeprecationWarning: is_AlgebraicField is deprecated; + use isinstance(..., sage.rings.abc.AlgebraicField instead + See https://trac.sagemath.org/32660 for details. [False, True, False, False, False] """ + from sage.misc.superseded import deprecation + deprecation(32660, 'is_AlgebraicField is deprecated; use isinstance(..., sage.rings.abc.AlgebraicField instead') return isinstance(F, AlgebraicField) @@ -2010,12 +2029,21 @@ def is_AlgebraicField_common(F): r""" Check whether ``F`` is an :class:`~AlgebraicField_common` instance. + This function is deprecated. Use :func:`isinstance` with + :class:`~sage.rings.abc.AlgebraicField_common` instead. + EXAMPLES:: sage: from sage.rings.qqbar import is_AlgebraicField_common sage: [is_AlgebraicField_common(x) for x in [AA, QQbar, None, 0, "spam"]] + doctest:warning... + DeprecationWarning: is_AlgebraicField_common is deprecated; + use isinstance(..., sage.rings.abc.AlgebraicField_common) instead + See https://trac.sagemath.org/32610 for details. [True, True, False, False, False] """ + from sage.misc.superseded import deprecation + deprecation(32610, 'is_AlgebraicField_common is deprecated; use isinstance(..., sage.rings.abc.AlgebraicField_common) instead') return isinstance(F, AlgebraicField_common) @@ -6551,7 +6579,7 @@ def __init__(self, poly): poly = QQy(poly) complex = False elif isinstance(B, AlgebraicField_common): - complex = is_AlgebraicField(poly.base_ring()) + complex = isinstance(poly.base_ring(), AlgebraicField) else: try: poly = poly.change_ring(AA) diff --git a/src/sage/rings/qqbar_decorators.py b/src/sage/rings/qqbar_decorators.py index 621e5ded0d7..e18fac73cdc 100644 --- a/src/sage/rings/qqbar_decorators.py +++ b/src/sage/rings/qqbar_decorators.py @@ -87,12 +87,12 @@ def wrapper(*args, **kwds): from sage.rings.polynomial.multi_polynomial import MPolynomial from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, is_PolynomialSequence from sage.rings.ideal import Ideal, Ideal_generic - from sage.rings.qqbar import is_AlgebraicField_common, number_field_elements_from_algebraics + from sage.rings.qqbar import AlgebraicField_common, number_field_elements_from_algebraics if not any(isinstance(a, (Polynomial, MPolynomial, Ideal_generic)) - and is_AlgebraicField_common(a.base_ring()) + and isinstance(a.base_ring(), AlgebraicField_common) or is_PolynomialSequence(a) - and is_AlgebraicField_common(a.ring().base_ring()) for a in args): + and isinstance(a.ring().base_ring(), AlgebraicField_common) for a in args): return func(*args, **kwds) polynomials = [] diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 5aab78bcb7b..4d7bd8f22cd 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -1524,8 +1524,9 @@ cdef class Expression(CommutativeRingElement): ValueError: cannot convert sqrt(-3) to int """ from sage.functions.all import floor, ceil + from sage.rings.real_mpfi import RIF try: - rif_self = sage.rings.all.RIF(self) + rif_self = RIF(self) except TypeError: raise ValueError("cannot convert %s to int" % self) if rif_self > 0 or (rif_self.contains_zero() and self > 0): @@ -3548,27 +3549,22 @@ cdef class Expression(CommutativeRingElement): if not self.is_relational(): raise ValueError("self must be a relation") cdef operators op = relational_operator(self._gobj) - from sage.rings.real_mpfi import is_RealIntervalField - from sage.rings.complex_interval_field import is_ComplexIntervalField - from sage.rings.all import RIF, CIF - from sage.rings.qqbar import is_AlgebraicField, is_AlgebraicRealField, AA, QQbar if domain is None: is_interval = True if self.lhs().is_algebraic() and self.rhs().is_algebraic(): if op == equal or op == not_equal: - domain = QQbar + from sage.rings.qqbar import QQbar as domain else: - domain = AA + from sage.rings.qqbar import AA as domain else: if op == equal or op == not_equal: - domain = CIF + from sage.rings.qqbar import CIF as domain else: - domain = RIF + from sage.rings.real_mpfi import RIF as domain else: - is_interval = (isinstance(domain, (sage.rings.abc.RealIntervalField, - sage.rings.abc.ComplexIntervalField)) - or is_AlgebraicField(domain) - or is_AlgebraicRealField(domain)) + is_interval = isinstance(domain, (sage.rings.abc.RealIntervalField, + sage.rings.abc.ComplexIntervalField, + sage.rings.abc.AlgebraicField_common)) zero = domain(0) diff = self.lhs() - self.rhs() vars = diff.variables() @@ -3621,6 +3617,7 @@ cdef class Expression(CommutativeRingElement): except (TypeError, ValueError, ArithmeticError, AttributeError) as ex: errors += 1 if k == errors > 3 and isinstance(domain, sage.rings.abc.ComplexIntervalField): + from sage.rings.real_mpfi import RIF domain = RIF.to_prec(domain.prec()) # we are plugging in random values above, don't be surprised # if something goes wrong... @@ -6644,8 +6641,9 @@ cdef class Expression(CommutativeRingElement): except (TypeError, AttributeError): pass from sage.functions.all import floor, ceil + from sage.rings.real_mpfi import RIF try: - rif_self = sage.rings.all.RIF(self) + rif_self = RIF(self) except TypeError: raise ValueError("could not convert %s to a real number" % self) half = 1 / sage.rings.integer.Integer(2) From 8d74803e163dc81648ec84813d5805ce8bf94eb5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 00:08:46 -0700 Subject: [PATCH 048/122] Add sage.rings.abc.NumberField_quadratic, deprecate is_QuadraticField --- src/sage/combinat/root_system/coxeter_type.py | 4 ++-- src/sage/groups/matrix_gps/coxeter_group.py | 5 +++-- src/sage/interfaces/polymake.py | 7 ++++--- src/sage/rings/abc.pyx | 14 +++++++------- src/sage/rings/complex_mpfr.pyx | 6 ++---- src/sage/rings/number_field/number_field.py | 11 ++++++++++- src/sage/rings/polynomial/polynomial_element.pyx | 16 +--------------- src/sage/rings/real_mpfi.pyx | 4 ++-- src/sage/schemes/elliptic_curves/heegner.py | 3 ++- 9 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index 4c29231d549..5130d33ad8f 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -21,12 +21,12 @@ from sage.misc.cachefunc import cached_method from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.combinat.root_system.cartan_type import CartanType +import sage.rings.abc from sage.matrix.args import SparseEntry from sage.matrix.all import Matrix from sage.symbolic.ring import SR from sage.structure.unique_representation import UniqueRepresentation from sage.structure.sage_object import SageObject -from sage.rings.number_field.number_field import is_QuadraticField class CoxeterType(SageObject, metaclass=ClasscallMetaclass): @@ -391,7 +391,7 @@ def val(x): return (E(2*x) + ~E(2*x)) / R(-2) else: return R(x) - elif is_QuadraticField(R): + elif isinstance(R, sage.rings.abc.NumberField_quadratic): def val(x): if x > -1: diff --git a/src/sage/groups/matrix_gps/coxeter_group.py b/src/sage/groups/matrix_gps/coxeter_group.py index 3a2781d8c24..ddf4136a223 100644 --- a/src/sage/groups/matrix_gps/coxeter_group.py +++ b/src/sage/groups/matrix_gps/coxeter_group.py @@ -27,10 +27,11 @@ from sage.matrix.args import SparseEntry from sage.matrix.matrix_space import MatrixSpace +import sage.rings.abc from sage.rings.integer_ring import ZZ from sage.rings.infinity import infinity from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField -from sage.rings.number_field.number_field import QuadraticField, is_QuadraticField +from sage.rings.number_field.number_field import QuadraticField from sage.misc.cachefunc import cached_method @@ -280,7 +281,7 @@ def val(x): return 2 else: return E(2 * x) + ~E(2 * x) - elif is_QuadraticField(base_ring): + elif isinstance(base_ring, sage.rings.abc.NumberField_quadratic): def val(x): if x == -1: diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 4111116598c..b4183a8ba31 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -332,8 +332,9 @@ def convert(y): r.__sage_dict = z # do this to avoid having the entries of the list be garbage collected return r - from sage.rings.all import Integer, Rational, RDF - from sage.rings.number_field.number_field import is_QuadraticField + from sage.rings.integer import Integer + from sage.rings.rational import Rational + from sage.rings.real_double import RDF def to_str(x): if isinstance(x, list): @@ -350,7 +351,7 @@ def to_str(x): except AttributeError: pass - if is_QuadraticField(parent): + if isinstance(parent, sage.rings.abc.NumberField_quadratic): return x._polymake_init_() try: if x.parent().is_exact(): diff --git a/src/sage/rings/abc.pyx b/src/sage/rings/abc.pyx index 15e195a4e1a..764f88ade61 100644 --- a/src/sage/rings/abc.pyx +++ b/src/sage/rings/abc.pyx @@ -2,31 +2,31 @@ Abstract base classes for rings """ -class AlgebraicField_common(Field): +class NumberField_quadratic(Field): r""" - Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField_common`. + Abstract base class for :class:`~sage.rings.number_field.number_field.NumberField_quadratic`. """ pass -class AlgebraicField(AlgebraicField_common): +class AlgebraicField_common(Field): r""" - Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField`. + Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField_common`. """ pass -class AlgebraicRealField(AlgebraicField_common): +class AlgebraicField(AlgebraicField_common): r""" - Abstract base class for :class:`~sage.rings.qqbar.AlgebraicRealField`. + Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField`. """ pass -cdef class RealField(Field): +class AlgebraicRealField(AlgebraicField_common): r""" Abstract base class for :class:`~sage.rings.real_mpfr.RealField_class`. """ diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 012344ea2a9..7cf9fd9f215 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -46,6 +46,7 @@ from sage.structure.parent_gens import ParentWithGens from sage.misc.sage_eval import sage_eval +import sage.rings.abc from sage.arith.constants cimport LOG_TEN_TWO_PLUS_EPSILON from . import ring, infinity from .integer cimport Integer @@ -63,7 +64,6 @@ gmpy2.import_gmpy2() # Some objects that are note imported at startup in order to break # circular imports -NumberField_quadratic = None NumberFieldElement_quadratic = None AlgebraicNumber_base = None AlgebraicNumber = None @@ -80,7 +80,6 @@ def late_import(): sage: sage.rings.complex_mpfr.late_import() """ - global NumberField_quadratic global NumberFieldElement_quadratic global AlgebraicNumber_base global AlgebraicNumber @@ -91,7 +90,6 @@ def late_import(): if NumberFieldElement_quadratic is None: import sage.rings.number_field.number_field import sage.rings.number_field.number_field_element_quadratic as nfeq - NumberField_quadratic = sage.rings.number_field.number_field.NumberField_quadratic NumberFieldElement_quadratic = nfeq.NumberFieldElement_quadratic import sage.rings.qqbar AlgebraicNumber_base = sage.rings.qqbar.AlgebraicNumber_base @@ -511,7 +509,7 @@ class ComplexField_class(sage.rings.abc.ComplexField): late_import() if isinstance(x, NumberFieldElement_quadratic): - if isinstance(x.parent(), NumberField_quadratic) and list(x.parent().polynomial()) == [1, 0, 1]: + if isinstance(x.parent(), sage.rings.abc.NumberField_quadratic) and list(x.parent().polynomial()) == [1, 0, 1]: (re, im) = list(x) return ComplexNumber(self, re, im) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 4b188823ac3..1b8ec5874c0 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -1041,10 +1041,17 @@ def is_QuadraticField(x): r""" Return True if x is of the quadratic *number* field type. + This function is deprecated. Use :func:`isinstance` with + :class:`~sage.rings.abc.NumberField_quadratic` instead. + EXAMPLES:: sage: from sage.rings.number_field.number_field import is_QuadraticField sage: is_QuadraticField(QuadraticField(5,'a')) + doctest:warning... + DeprecationWarning: is_QuadraticField is deprecated; + use isinstance(..., sage.rings.abc.NumberField_quadratic instead + See https://trac.sagemath.org/32660 for details. True sage: is_QuadraticField(NumberField(x^2 - 5, 'b')) True @@ -1057,6 +1064,8 @@ def is_QuadraticField(x): sage: is_QuadraticField(GF(9,'a')) False """ + from sage.misc.superseded import deprecation + deprecation(32660, 'is_QuadraticField is deprecated; use isinstance(..., sage.rings.abc.NumberField_quadratic instead') return isinstance(x, NumberField_quadratic) @@ -11709,7 +11718,7 @@ def roots_of_unity(self): return v -class NumberField_quadratic(NumberField_absolute): +class NumberField_quadratic(NumberField_absolute, sage.rings.abc.NumberField_quadratic): r""" Create a quadratic extension of the rational field. diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 7d47f0dbeb1..6fc5c92a04f 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -171,18 +171,6 @@ from .polynomial_compiled cimport CompiledPolynomialFunction from sage.rings.polynomial.polydict cimport ETuple -cdef object NumberField_quadratic - -cdef void late_import(): - # A hack to avoid circular imports. - global NumberField_quadratic - - if NumberField_quadratic is not None: - return - - import sage.rings.number_field.number_field - NumberField_quadratic = sage.rings.number_field.number_field.NumberField_quadratic - cdef class Polynomial(CommutativeAlgebraElement): """ @@ -7836,8 +7824,6 @@ cdef class Polynomial(CommutativeAlgebraElement): L = K if ring is None else ring - late_import() - input_fp = isinstance(K, (sage.rings.abc.RealField, sage.rings.abc.ComplexField, sage.rings.abc.RealDoubleField, @@ -7848,7 +7834,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage.rings.abc.ComplexDoubleField)) input_complex = isinstance(K, (sage.rings.abc.ComplexField, sage.rings.abc.ComplexDoubleField)) output_complex = isinstance(L, (sage.rings.abc.ComplexField, sage.rings.abc.ComplexDoubleField)) - input_gaussian = (isinstance(K, NumberField_quadratic) + input_gaussian = (isinstance(K, sage.rings.abc.NumberField_quadratic) and list(K.polynomial()) == [1, 0, 1]) if input_fp and output_fp: diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index ce83e1ddf4e..78ab958c048 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -278,6 +278,7 @@ from .integer_ring import ZZ from .rational_field import QQ from sage.categories.morphism cimport Map +cimport sage.rings.abc cimport sage.rings.real_mpfr as real_mpfr import math # for log @@ -813,8 +814,7 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField): return True if isinstance(S, RealIntervalField_class): return (S).__prec >= prec - from .number_field.number_field import NumberField_quadratic - if isinstance(S, NumberField_quadratic): + if isinstance(S, sage.rings.abc.NumberField_quadratic): return S.discriminant() > 0 # If coercion to RR is possible and there is a _real_mpfi_ diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index fc7d07086a8..1909bb54c44 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -101,6 +101,7 @@ from sage.structure.richcmp import (richcmp_method, richcmp, richcmp_not_equal, rich_to_bool) +import sage.rings.abc import sage.rings.number_field.number_field_element import sage.rings.number_field.number_field as number_field import sage.rings.all as rings @@ -1107,7 +1108,7 @@ def _base_is_quad_imag_field(self): sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_quad_imag_field() False """ - return number_field.is_QuadraticField(self.__base) + return isinstance(self.__base, sage.rings.abc.NumberField_quadratic) def is_kolyvagin(self): """ From f4369f544e7e739d0414e2d60c13f6de86aeca6b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 00:48:26 -0700 Subject: [PATCH 049/122] Add sage.rings.abc.NumberField_cyclotomic, deprecate is_CyclotomicField --- src/sage/groups/misc_gps/argument_groups.py | 4 ++-- src/sage/matrix/matrix_space.py | 4 ++-- src/sage/modular/dirichlet.py | 6 +++--- src/sage/rings/abc.pyx | 8 ++++++++ src/sage/rings/number_field/number_field.py | 13 +++++++++++-- src/sage/rings/universal_cyclotomic_field.py | 20 +++++++++++--------- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/sage/groups/misc_gps/argument_groups.py b/src/sage/groups/misc_gps/argument_groups.py index 818185f783e..5ff64d4ddee 100644 --- a/src/sage/groups/misc_gps/argument_groups.py +++ b/src/sage/groups/misc_gps/argument_groups.py @@ -735,9 +735,9 @@ def _element_constructor_(self, data, exponent=None, **kwds): zeta2^5 """ from sage.groups.generic import discrete_log + import sage.rings.abc from sage.rings.asymptotic.misc import combine_exceptions from sage.rings.rational_field import QQ - from sage.rings.number_field.number_field import NumberField_cyclotomic if exponent is None: if isinstance(data, int) and data == 0: @@ -769,7 +769,7 @@ def _element_constructor_(self, data, exponent=None, **kwds): elif isinstance(P, UnitCircleGroup): exponent = data.exponent - elif isinstance(P, NumberField_cyclotomic): + elif isinstance(P, sage.rings.abc.NumberField_cyclotomic): zeta = P.gen() n = zeta.multiplicative_order() try: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index eabba820926..2f7a3566333 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -243,7 +243,7 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): if R.order() < matrix_modn_dense_double.MAX_MODULUS: return matrix_modn_dense_double.Matrix_modn_dense_double - if sage.rings.number_field.number_field.is_CyclotomicField(R): + if isinstance(R, sage.rings.abc.NumberField_cyclotomic): from . import matrix_cyclo_dense return matrix_cyclo_dense.Matrix_cyclo_dense @@ -296,7 +296,7 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): raise ValueError("'numpy' matrices are only available over RDF and CDF") if implementation == 'rational': - if sage.rings.number_field.number_field.is_CyclotomicField(R): + if isinstance(R, sage.rings.abc.NumberField_cyclotomic): from . import matrix_cyclo_dense return matrix_cyclo_dense.Matrix_cyclo_dense raise ValueError("'rational' matrices are only available over a cyclotomic field") diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index c35e712de21..143157de883 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -1349,7 +1349,7 @@ def gauss_sum(self, a=1): elif isinstance(K, sage.rings.abc.AlgebraicField): L = K zeta = L.zeta(m) - elif number_field.is_CyclotomicField(K) or is_RationalField(K): + elif isinstance(K, sage.rings.abc.NumberField_cyclotomic) or is_RationalField(K): chi = chi.minimize_base_ring() n = lcm(m, G.zeta_order()) L = rings.CyclotomicField(n) @@ -1430,7 +1430,7 @@ def phi(t): from sage.rings.complex_mpfr import ComplexField CC = ComplexField(prec) phi = CC.coerce_map_from(K) - elif number_field.is_CyclotomicField(K) or is_RationalField(K): + elif isinstance(K, sage.rings.abc.NumberField_cyclotomic) or is_RationalField(K): phi = K.complex_embedding(prec) CC = phi.codomain() else: @@ -1649,7 +1649,7 @@ def kloosterman_sum_numerical(self, prec=53, a=1, b=0): """ G = self.parent() K = G.base_ring() - if not (number_field.is_CyclotomicField(K) or is_RationalField(K)): + if not (isinstance(K, sage.rings.abc.NumberField_cyclotomic) or is_RationalField(K)): raise NotImplementedError("Kloosterman sums only currently implemented when the base ring is a cyclotomic field or QQ.") phi = K.complex_embedding(prec) CC = phi.codomain() diff --git a/src/sage/rings/abc.pyx b/src/sage/rings/abc.pyx index 764f88ade61..10073c6bcf9 100644 --- a/src/sage/rings/abc.pyx +++ b/src/sage/rings/abc.pyx @@ -10,6 +10,14 @@ class NumberField_quadratic(Field): pass +class NumberField_cyclotomic(Field): + r""" + Abstract base class for :class:`~sage.rings.number_field.number_field.NumberField_cyclotomic`. + """ + + pass + + class AlgebraicField_common(Field): r""" Abstract base class for :class:`~sage.rings.qqbar.AlgebraicField_common`. diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 1b8ec5874c0..7c8272562d1 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -1238,10 +1238,17 @@ def is_CyclotomicField(x): number field that just happens to be isomorphic to a cyclotomic field. + This function is deprecated. Use :func:`isinstance` with + :class:`~sage.rings.abc.NumberField_cyclotomic` instead. + EXAMPLES:: sage: from sage.rings.number_field.number_field import is_CyclotomicField sage: is_CyclotomicField(NumberField(x^2 + 1,'zeta4')) + doctest:warning... + DeprecationWarning: is_CyclotomicField is deprecated; + use isinstance(..., sage.rings.abc.NumberField_cyclotomic instead + See https://trac.sagemath.org/32660 for details. False sage: is_CyclotomicField(CyclotomicField(4)) True @@ -1252,6 +1259,8 @@ def is_CyclotomicField(x): sage: is_CyclotomicField(7) False """ + from sage.misc.superseded import deprecation + deprecation(32660, 'is_CyclotomicField is deprecated; use isinstance(..., sage.rings.abc.NumberField_cyclotomic instead') return isinstance(x, NumberField_cyclotomic) @@ -10355,7 +10364,7 @@ def _factor_univariate_polynomial(self, poly, **kwargs): return poly._factor_pari_helper(G) -class NumberField_cyclotomic(NumberField_absolute): +class NumberField_cyclotomic(NumberField_absolute, sage.rings.abc.NumberField_cyclotomic): """ Create a cyclotomic extension of the rational field. @@ -11258,7 +11267,7 @@ def is_isomorphic(self, other): sage: K.is_isomorphic(CyclotomicField(8)) False """ - if is_CyclotomicField(other): + if isinstance(other, NumberField_cyclotomic): return self.zeta_order() == other.zeta_order() return NumberField_generic.is_isomorphic(self, other) diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 9c7ed719378..2f5c930d9ff 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -1534,14 +1534,16 @@ def _element_constructor_(self, elt): return self.element_class(self, obj) # late import to avoid slowing down the above conversions - from sage.rings.number_field.number_field_element import NumberFieldElement - from sage.rings.number_field.number_field import NumberField_cyclotomic, CyclotomicField + import sage.rings.abc P = parent(elt) - if isinstance(elt, NumberFieldElement) and isinstance(P, NumberField_cyclotomic): - n = P.gen().multiplicative_order() - elt = CyclotomicField(n)(elt) - return sum(c * self.gen(n, i) - for i, c in enumerate(elt._coefficients())) + if isinstance(P, sage.rings.abc.NumberField_cyclotomic): + from sage.rings.number_field.number_field_element import NumberFieldElement + if isinstance(elt, NumberFieldElement): + from sage.rings.number_field.number_field import CyclotomicField + n = P.gen().multiplicative_order() + elt = CyclotomicField(n)(elt) + return sum(c * self.gen(n, i) + for i, c in enumerate(elt._coefficients())) if hasattr(elt, '_algebraic_'): return elt._algebraic_(self) @@ -1568,8 +1570,8 @@ def _coerce_map_from_(self, other): """ if other is ZZ or other is QQ: return True - from sage.rings.number_field.number_field import NumberField_cyclotomic - if isinstance(other, NumberField_cyclotomic): + import sage.rings.abc + if isinstance(other, sage.rings.abc.NumberField_cyclotomic): return True def _factor_univariate_polynomial(self, f): From 9883de7f68cc58dfd3428dd1b180a708cb27b50a Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 16 Oct 2021 12:01:38 +0200 Subject: [PATCH 050/122] trac #32695: fix internet doctests --- src/sage/databases/oeis.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index ead5a2174ef..91d2a52e501 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -344,7 +344,7 @@ class OEIS: sage: fibo.formulas()[4] # optional -- internet 'F(n) = F(n-1) + F(n-2) = -(-1)^n F(-n).' - sage: fibo.comments()[1] # optional -- internet + sage: fibo.comments()[6] # optional -- internet "F(n+2) = number of binary sequences of length n that have no consecutive 0's." @@ -539,10 +539,10 @@ def find_by_subsequence(self, subsequence, max_results=3, first_result=0): EXAMPLES:: - sage: oeis.find_by_subsequence([2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]) # optional -- internet + sage: oeis.find_by_subsequence([2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]) # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. 1: A212804: Expansion of (1 - x)/(1 - x - x^2). - 2: A177194: Fibonacci numbers whose decimal expansion does not contain any digit 0. + 2: A020695: Pisot sequence E(2,3). sage: fibo = _[0] ; fibo # optional -- internet A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. @@ -1504,7 +1504,7 @@ def references(self): A007540: Wilson primes: primes p such that (p-1)! == -1 (mod p^2). sage: w.references() # optional -- internet - ...A. H. Beiler, Recreations in the Theory of Numbers, Dover, NY, 1964, p. 52. + ...Albert H. Beiler, Recreations in the Theory of Numbers, Dover, NY, 1964, p. 52. ... TESTS:: @@ -1639,7 +1639,7 @@ def cross_references(self, fetch=False): sage: nbalanced.cross_references(fetch=True) # optional -- internet 0: A049703: a(0) = 0; for n>0, a(n) = A005598(n)/2. 1: A049695: Array T read by diagonals; ... - 2: A103116: a(n) = A005598(n) - 1. + 2: A103116: a(n) = Sum_{i=1..n} (n-i+1)*phi(i). 3: A000010: Euler totient function phi(n): count numbers <= n and prime to n. sage: phi = _[3] # optional -- internet @@ -1721,10 +1721,15 @@ def comments(self): sage: f = oeis(45) ; f # optional -- internet A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - sage: f.comments()[:3] # optional -- internet - 0: Also sometimes called Lamé's sequence. - 1: F(n+2) = number of binary sequences of length n that have no consecutive 0's. - 2: F(n+2) = number of subsets of {1,2,...,n} that contain no consecutive integers. + sage: f.comments()[:8] # optional -- internet + 0: D. E. Knuth writes... + 1: In keeping with historical accounts... + 2: Susantha Goonatilake writes... + 3: Also sometimes called Hemachandra numbers. + 4: Also sometimes called Lamé's sequence. + 5: ... + 6: F(n+2) = number of binary sequences of length n that have no consecutive 0's. + 7: F(n+2) = number of subsets of {1,2,...,n} that contain no consecutive integers. TESTS:: From eac63034ac8e190c61644ec510346fee6de17ff1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 19:24:37 -0700 Subject: [PATCH 051/122] src/sage/interfaces/polymake.py: Add missing import --- src/sage/interfaces/polymake.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index b4183a8ba31..6e20f555a70 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -332,6 +332,7 @@ def convert(y): r.__sage_dict = z # do this to avoid having the entries of the list be garbage collected return r + import sage.rings.abc from sage.rings.integer import Integer from sage.rings.rational import Rational from sage.rings.real_double import RDF From 49dc68abd409d07fb0b73cc7c1cc159c7439b810 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 20:19:36 -0700 Subject: [PATCH 052/122] sage.rings.abc: Add SymbolicRing, CallableSymbolicExpressionRing --- src/sage/rings/abc.pyx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/rings/abc.pyx b/src/sage/rings/abc.pyx index 147dcb4f089..530408d1b18 100644 --- a/src/sage/rings/abc.pyx +++ b/src/sage/rings/abc.pyx @@ -47,3 +47,19 @@ class Order: """ pass + + +cdef class SymbolicRing(CommutativeRing): + r""" + Abstract base class for :class:`~sage.rings.symbolic.ring.SymbolicRing`. + """ + + pass + + +class CallableSymbolicExpressionRing(SymbolicRing): + r""" + Abstract base class for :class:`~sage.rings.symbolic.callable.CallableSymbolicExpressionRing_class`. + """ + + pass From 5d50f092e297e92f014584fe2165ce4ae01c867b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 20:19:06 -0700 Subject: [PATCH 053/122] sage.symbolic: Use sage.rings.abc.{SymbolicRing,CallableSymbolicExpressionRing} --- src/sage/symbolic/callable.py | 3 ++- src/sage/symbolic/ring.pxd | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/symbolic/callable.py b/src/sage/symbolic/callable.py index f1a45811db8..dd0c5e1f67b 100644 --- a/src/sage/symbolic/callable.py +++ b/src/sage/symbolic/callable.py @@ -61,6 +61,7 @@ SyntaxError: can...t assign to function call """ +import sage.rings.abc from sage.symbolic.ring import SymbolicRing, SR from sage.categories.pushout import ConstructionFunctor @@ -267,7 +268,7 @@ def unify_arguments(self, x): return tuple(new_list) -class CallableSymbolicExpressionRing_class(SymbolicRing): +class CallableSymbolicExpressionRing_class(SymbolicRing, sage.rings.abc.CallableSymbolicExpressionRing): def __init__(self, arguments): """ EXAMPLES: diff --git a/src/sage/symbolic/ring.pxd b/src/sage/symbolic/ring.pxd index ed358c4a3c7..9e628098dd1 100644 --- a/src/sage/symbolic/ring.pxd +++ b/src/sage/symbolic/ring.pxd @@ -1,4 +1,4 @@ -from sage.rings.ring cimport CommutativeRing +cimport sage.rings.abc -cdef class SymbolicRing(CommutativeRing): +cdef class SymbolicRing(sage.rings.abc.SymbolicRing): cdef public dict symbols From a862442d66f3fcfebb310c9a264fb274a26965d0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 20:20:17 -0700 Subject: [PATCH 054/122] src/sage/dynamics/arithmetic_dynamics/affine_ds.py: Use sage.rings.abc.SymbolicRing --- src/sage/dynamics/arithmetic_dynamics/affine_ds.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py index 116ee4e12b9..84ddb002252 100644 --- a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py @@ -56,9 +56,9 @@ class initialization directly. from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.structure.element import get_coercion_model -from sage.symbolic.ring import is_SymbolicExpressionRing -from sage.symbolic.ring import var -from sage.symbolic.ring import SR + +import sage.rings.abc + class DynamicalSystem_affine(SchemeMorphism_polynomial_affine_space, DynamicalSystem): @@ -275,7 +275,7 @@ def __classcall_private__(cls, morphism_or_polys, domain=None): else: polys = [PR(poly) for poly in polys] if domain is None: - if PR is SR: + if isinstance(PR, sage.rings.abc.SymbolicRing): raise TypeError("Symbolic Ring cannot be the base ring") if fraction_field: PR = PR.ring() @@ -292,7 +292,7 @@ def __classcall_private__(cls, morphism_or_polys, domain=None): if len(polys) != domain.ambient_space().coordinate_ring().ngens(): raise ValueError('Number of polys does not match dimension of {}'.format(domain)) R = domain.base_ring() - if R is SR: + if isinstance(R, sage.rings.abc.SymbolicRing): raise TypeError("Symbolic Ring cannot be the base ring") if not is_AffineSpace(domain) and not isinstance(domain, AlgebraicScheme_subscheme_affine): raise ValueError('"domain" must be an affine scheme') @@ -529,7 +529,8 @@ def dynatomic_polynomial(self, period): F = G.dynatomic_polynomial(period) T = G.domain().coordinate_ring() S = self.domain().coordinate_ring() - if is_SymbolicExpressionRing(F.parent()): + if isinstance(F.parent(), sage.rings.abc.SymbolicRing): + from sage.symbolic.ring import var u = var(self.domain().coordinate_ring().variable_name()) return F.subs({F.variables()[0]:u,F.variables()[1]:1}) elif T(F.denominator()).degree() == 0: From 24cd270773ef77900747bb0d5a6cbf7683b79a64 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 20:20:31 -0700 Subject: [PATCH 055/122] src/sage/matrix/matrix2.pyx: Use sage.rings.abc.SymbolicRing --- src/sage/matrix/matrix2.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index f94e77474c8..ae7be68d55a 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -1984,8 +1984,6 @@ cdef class Matrix(Matrix1): sage: A.determinant() == B.determinant() True """ - from sage.symbolic.ring import is_SymbolicExpressionRing - cdef Py_ssize_t n n = self._ncols @@ -2074,7 +2072,7 @@ cdef class Matrix(Matrix1): # is then assumed to not be a variable in the symbolic ring. But this # resulted in further exceptions/ errors. - var = 'A0123456789' if is_SymbolicExpressionRing(R) else 'x' + var = 'A0123456789' if isinstance(R, sage.rings.abc.SymbolicRing) else 'x' try: charp = self.charpoly(var, algorithm="df") except ValueError: From 5fb862e568f0635c93267847079adc8695e83295 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 20:49:07 -0700 Subject: [PATCH 056/122] Deprecate is_[Callable]SymbolicExpressionRing, replace uses by isinstance(..., sage.rings.abc....) --- src/sage/modules/free_module.py | 8 ++++---- src/sage/modules/free_module_element.pyx | 6 +++--- src/sage/symbolic/callable.py | 8 +++++++- src/sage/symbolic/expression.pyx | 5 ++--- src/sage/symbolic/ring.pyx | 10 +++++++++- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 1e3c73da128..ba5e1eb6ccb 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -7448,12 +7448,12 @@ def element_class(R, is_sparse): return sage.modules.vector_real_double_dense.Vector_real_double_dense elif isinstance(R, sage.rings.abc.ComplexDoubleField) and not is_sparse: return sage.modules.vector_complex_double_dense.Vector_complex_double_dense - elif sage.symbolic.ring.is_SymbolicExpressionRing(R) and not is_sparse: - import sage.modules.vector_symbolic_dense - return sage.modules.vector_symbolic_dense.Vector_symbolic_dense - elif sage.symbolic.callable.is_CallableSymbolicExpressionRing(R) and not is_sparse: + elif isinstance(R, sage.rings.abc.CallableSymbolicExpressionRing) and not is_sparse: import sage.modules.vector_callable_symbolic_dense return sage.modules.vector_callable_symbolic_dense.Vector_callable_symbolic_dense + elif isinstance(R, sage.rings.abc.SymbolicRing) and not is_sparse: + import sage.modules.vector_symbolic_dense + return sage.modules.vector_symbolic_dense.Vector_symbolic_dense else: if is_sparse: return free_module_element.FreeModuleElement_generic_sparse diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 21d898c117c..4be7ff4ede4 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -118,6 +118,7 @@ from sage.structure.element cimport Element, ModuleElement, RingElement, Vector from sage.structure.element import canonical_coercion from sage.structure.richcmp cimport richcmp_not_equal, richcmp, rich_to_bool +cimport sage.rings.abc from sage.rings.ring import is_Ring from sage.rings.infinity import Infinity, AnInfinity from sage.rings.integer_ring import ZZ @@ -3933,9 +3934,8 @@ cdef class FreeModuleElement(Vector): # abstract base class (r, theta) |--> r*cos(theta)^2 + r*sin(theta)^2 """ if var is None: - from sage.symbolic.callable import is_CallableSymbolicExpressionRing - from sage.calculus.all import jacobian - if is_CallableSymbolicExpressionRing(self.coordinate_ring()): + if isinstance(self.coordinate_ring(), sage.rings.abc.CallableSymbolicExpressionRing): + from sage.calculus.all import jacobian return jacobian(self, self.coordinate_ring().arguments()) else: raise ValueError("No differentiation variable specified.") diff --git a/src/sage/symbolic/callable.py b/src/sage/symbolic/callable.py index dd0c5e1f67b..42d24bbce5f 100644 --- a/src/sage/symbolic/callable.py +++ b/src/sage/symbolic/callable.py @@ -82,12 +82,18 @@ def is_CallableSymbolicExpressionRing(x): sage: from sage.symbolic.callable import is_CallableSymbolicExpressionRing sage: is_CallableSymbolicExpressionRing(QQ) + doctest:warning... + DeprecationWarning: is_CallableSymbolicExpressionRing is deprecated; + use isinstance(..., sage.rings.abc.CallableSymbolicExpressionRing instead + See https://trac.sagemath.org/32665 for details. False sage: var('x,y,z') (x, y, z) sage: is_CallableSymbolicExpressionRing(CallableSymbolicExpressionRing((x,y,z))) True """ + from sage.misc.superseded import deprecation + deprecation(32665, 'is_CallableSymbolicExpressionRing is deprecated; use isinstance(..., sage.rings.abc.CallableSymbolicExpressionRing instead') return isinstance(x, CallableSymbolicExpressionRing_class) def is_CallableSymbolicExpression(x): @@ -301,7 +307,7 @@ def _coerce_map_from_(self, R): sage: g.parent().has_coerce_map_from(f.parent()) True """ - if is_CallableSymbolicExpressionRing(R): + if isinstance(R, CallableSymbolicExpressionRing): args = self.arguments() if all(a in args for a in R.arguments()): return True diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index d7932ea3486..6966a183590 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -13080,10 +13080,8 @@ cdef class Expression(Expression_abc): """ from sage.symbolic.integration.integral import \ integral, _normalize_integral_input - from sage.symbolic.callable import \ - CallableSymbolicExpressionRing, is_CallableSymbolicExpressionRing R = self._parent - if is_CallableSymbolicExpressionRing(R): + if isinstance(R, sage.rings.abc.CallableSymbolicExpressionRing): f = SR(self) f, v, a, b = _normalize_integral_input(f, *args) # Definite integral with respect to a positional variable. @@ -13092,6 +13090,7 @@ cdef class Expression(Expression_abc): arguments.remove(v) if arguments: arguments = tuple(arguments) + from sage.symbolic.callable import CallableSymbolicExpressionRing R = CallableSymbolicExpressionRing(arguments, check=False) else: # all arguments are gone R = SR diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index ae1efb6e0aa..b5f53c07cf0 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -1309,7 +1309,13 @@ def the_SymbolicRing(): def is_SymbolicExpressionRing(R): """ - Returns True if *R* is the symbolic expression ring. + Returns True if ``R`` is the symbolic expression ring. + + This function is deprecated. Instead, either use ``R is SR`` (to + test whether ``R`` is the unique symbolic ring ``SR``); or + ``isinstance`` with :class:`~sage.rings.abc.SymbolicRing` + (when also symbolic subrings and callable symbolic rings should + be accepted). EXAMPLES:: @@ -1319,6 +1325,8 @@ def is_SymbolicExpressionRing(R): sage: is_SymbolicExpressionRing(SR) True """ + from sage.misc.superseded import deprecation + deprecation(32665, 'is_SymbolicExpressionRing is deprecated; use "... is SR" or isinstance(..., sage.rings.abc.SymbolicRing instead') return R is SR def var(name, **kwds): From ee4bd52cfa12357872281a57ec96f63eb2b96a18 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 21:27:05 -0700 Subject: [PATCH 057/122] sage.rings.abc, sage.symbolic.ring: Fixup --- src/sage/modules/free_module_element.pyx | 2 +- src/sage/rings/abc.pxd | 7 ++++++- src/sage/symbolic/ring.pyx | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 4be7ff4ede4..0e3e55f4a23 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -118,7 +118,7 @@ from sage.structure.element cimport Element, ModuleElement, RingElement, Vector from sage.structure.element import canonical_coercion from sage.structure.richcmp cimport richcmp_not_equal, richcmp, rich_to_bool -cimport sage.rings.abc +import sage.rings.abc from sage.rings.ring import is_Ring from sage.rings.infinity import Infinity, AnInfinity from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/abc.pxd b/src/sage/rings/abc.pxd index 5b2aec37eff..a53b512d62f 100644 --- a/src/sage/rings/abc.pxd +++ b/src/sage/rings/abc.pxd @@ -1,4 +1,4 @@ -from .ring cimport Field +from .ring cimport CommutativeRing, Field cdef class RealField(Field): @@ -23,3 +23,8 @@ cdef class ComplexField(Field): cdef class ComplexDoubleField(Field): pass + + +cdef class SymbolicRing(CommutativeRing): + + pass diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index b5f53c07cf0..128ff05a8e1 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -32,6 +32,8 @@ The symbolic ring # **************************************************************************** from sage.rings.integer cimport Integer +from sage.rings.ring cimport CommutativeRing + import sage.rings.abc from sage.symbolic.expression cimport ( @@ -61,7 +63,7 @@ KEYWORDS = set(keyword.kwlist).union(['exec', 'print', 'None', 'True', 'False', 'nonlocal']) -cdef class SymbolicRing(CommutativeRing): +cdef class SymbolicRing(sage.rings.abc.SymbolicRing): """ Symbolic Ring, parent object for all symbolic expressions. """ From b484d51976ddc0aebd6072faa9986512a8090d53 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 21:55:56 -0700 Subject: [PATCH 058/122] src/sage/symbolic/callable.py: Fixup --- src/sage/symbolic/callable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/callable.py b/src/sage/symbolic/callable.py index 42d24bbce5f..5f3a7bea659 100644 --- a/src/sage/symbolic/callable.py +++ b/src/sage/symbolic/callable.py @@ -307,7 +307,7 @@ def _coerce_map_from_(self, R): sage: g.parent().has_coerce_map_from(f.parent()) True """ - if isinstance(R, CallableSymbolicExpressionRing): + if isinstance(R, CallableSymbolicExpressionRing_class): args = self.arguments() if all(a in args for a in R.arguments()): return True From fad87c08a90d8b13ba4a48102b2faca3911c4cf6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 21:56:17 -0700 Subject: [PATCH 059/122] Expression.is_callable: New --- src/sage/symbolic/expression.pyx | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 6966a183590..c43196f4b48 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3124,6 +3124,22 @@ cdef class Expression(Expression_abc): return obj.is_square() + def is_callable(self): + r""" + Return ``True`` if ``self`` is a callable symbolic expression. + + EXAMPLES:: + + sage: var('a x y z') + (a, x, y, z) + sage: f(x, y) = a + 2*x + 3*y + z + sage: f.is_callable() + True + sage: (a+2*x).is_callable() + False + """ + return isinstance(self.parent(), sage.rings.abc.CallableSymbolicExpressionRing) + def left_hand_side(self): """ If self is a relational expression, return the left hand side @@ -4632,7 +4648,7 @@ cdef class Expression(Expression_abc): symb = vars[0] elif len(vars) == 0: return self._parent(0) - elif sage.symbolic.callable.is_CallableSymbolicExpression(self): + elif self.is_callable(): return self.gradient() else: raise ValueError("No differentiation variable specified.") @@ -12714,7 +12730,6 @@ cdef class Expression(Expression_abc): sage: plot(f,0,1) Graphics object consisting of 1 graphics primitive """ - from sage.symbolic.callable import is_CallableSymbolicExpression from sage.symbolic.ring import is_SymbolicVariable from sage.plot.plot import plot @@ -12730,7 +12745,7 @@ cdef class Expression(Expression_abc): break if param is None: - if is_CallableSymbolicExpression(self): + if self.is_callable(): A = self.arguments() if len(A) == 0: raise ValueError("function has no input arguments") From 4bc059ba6807fb35cdfc04bf4d0b5a51bd9def20 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 16 Oct 2021 21:59:07 -0700 Subject: [PATCH 060/122] src/sage/ext/fast_callable.pyx: Remove use of is_CallableSymbolicExpression --- src/sage/ext/fast_callable.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index d54d501226c..da1ad9ed5b3 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -428,13 +428,11 @@ def fast_callable(x, domain=None, vars=None, et = x vars = et._etb._vars else: - from sage.symbolic.callable import is_CallableSymbolicExpression - if not vars: # fast_float passes empty list/tuple vars = None - if is_CallableSymbolicExpression(x): + if isinstance(x, Expression_abc) and x.is_callable(): if vars is None: vars = x.arguments() if expect_one_var and len(vars) != 1: From 8624925fadce22241a0e7abdc9952ee4bbcf4a0a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 09:20:31 -0700 Subject: [PATCH 061/122] src/sage/symbolic/ring.pyx: Update doctest output with deprecation warning --- src/sage/symbolic/ring.pyx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 128ff05a8e1..115c1361791 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -1323,6 +1323,10 @@ def is_SymbolicExpressionRing(R): sage: from sage.symbolic.ring import is_SymbolicExpressionRing sage: is_SymbolicExpressionRing(ZZ) + doctest:warning... + DeprecationWarning: is_SymbolicExpressionRing is deprecated; + use "... is SR" or isinstance(..., sage.rings.abc.SymbolicRing instead + See https://trac.sagemath.org/32665 for details. False sage: is_SymbolicExpressionRing(SR) True From 37da733abd71b634e542334ec2d460bff002e444 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 09:24:05 -0700 Subject: [PATCH 062/122] src/sage/sets/condition_set.py: Remove use of is_CallableSymbolicExpression --- src/sage/sets/condition_set.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 80ff63d2260..34265775dd3 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -20,14 +20,9 @@ from sage.misc.cachefunc import cached_method from sage.misc.misc import _stable_uniq from sage.structure.element import Expression - -try: - from sage.symbolic.callable import is_CallableSymbolicExpression -except ImportError: - is_CallableSymbolicExpression = lambda x: False - from .set import Set, Set_base, Set_boolean_operators, Set_add_sub_operators + class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_operators, UniqueRepresentation): r""" @@ -160,7 +155,7 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat other_predicates = [] for predicate in predicates: - if is_CallableSymbolicExpression(predicate): + if isinstance(predicate, Expression) and predicate.is_callable(): if names is None: names = tuple(str(var) for var in predicate.args()) elif len(names) != len(predicate.args()): @@ -271,7 +266,7 @@ def _repr_condition(self, predicate): sage: ZeroDimButNotNullary._repr_condition(ZeroDimButNotNullary._predicates[0]) 't > 0' """ - if is_CallableSymbolicExpression(predicate): + if isinstance(predicate, Expression) and predicate.is_callable(): args = self.arguments() if len(args) == 1: args = args[0] @@ -364,7 +359,7 @@ def _call_predicate(self, predicate, element): sage: Nullary._call_predicate(predicate, element) t > 0 """ - if is_CallableSymbolicExpression(predicate): + if isinstance(predicate, Expression) and predicate.is_callable(): if len(predicate.arguments()) != 1: return predicate(*element) return predicate(element) From 80a8f9ec657528c814767bfd0da0500a7d650f2f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 09:36:35 -0700 Subject: [PATCH 063/122] sage.plot: Remove use of is_CallableSymbolicExpression, is_SymbolicEquation --- src/sage/plot/contour_plot.py | 4 ++-- src/sage/plot/misc.py | 6 ++---- src/sage/plot/plot3d/plot3d.py | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index 27577d396d6..690311744e7 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -1195,8 +1195,8 @@ def f(x,y): ... ValueError: only one of color or rgbcolor should be specified """ - from sage.symbolic.expression import is_SymbolicEquation - if is_SymbolicEquation(f): + from sage.structure.element import Expression + if isinstance(f, Expression) and f.is_relational(): if f.operator() != operator.eq: raise ValueError("input to implicit plot must be function " "or equation") diff --git a/src/sage/plot/misc.py b/src/sage/plot/misc.py index b57976552b6..edab4ce4183 100644 --- a/src/sage/plot/misc.py +++ b/src/sage/plot/misc.py @@ -15,7 +15,7 @@ from sage.ext.fast_eval import fast_float -from sage.structure.element import is_Vector +from sage.structure.element import is_Vector, Expression def setup_for_eval_on_grid(funcs, ranges, plot_points=None, return_vars=False): """ @@ -187,15 +187,13 @@ def unify_arguments(funcs): sage: sage.plot.misc.unify_arguments((x+y,x-y)) ((x, y), (x, y)) """ - from sage.symbolic.callable import is_CallableSymbolicExpression - vars=set() free_variables=set() if not isinstance(funcs, (list, tuple)): funcs = [funcs] for f in funcs: - if is_CallableSymbolicExpression(f): + if isinstance(f, Expression) and f.is_callable(): f_args = set(f.arguments()) vars.update(f_args) else: diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 9678373657c..0fccc9cb1ec 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -1046,13 +1046,13 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): Graphics3d Object """ if transformation is not None: - params=None - from sage.symbolic.callable import is_CallableSymbolicExpression + params = None + from sage.structure.element import Expression # First, determine the parameters for f (from the first item of urange # and vrange, preferably). if len(urange) == 3 and len(vrange) == 3: params = (urange[0], vrange[0]) - elif is_CallableSymbolicExpression(f): + elif isinstance(f, Expression) and f.is_callable(): params = f.variables() from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense From a287531e1dd03d32d554735a26966852c3c56057 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 09:51:50 -0700 Subject: [PATCH 064/122] src/sage/schemes/elliptic_curves/constructor.py: Remove use of SR, is_SymbolicEquation; add test for symbolic input --- .../schemes/elliptic_curves/constructor.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 90bc4519082..bd7cfc8239d 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -36,10 +36,8 @@ _Fields = Fields() from sage.structure.sequence import Sequence -from sage.structure.element import parent +from sage.structure.element import parent, Expression from sage.structure.factory import UniqueFactory -from sage.symbolic.ring import SR -from sage.symbolic.expression import is_SymbolicEquation class EllipticCurveFactory(UniqueFactory): @@ -385,6 +383,20 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, incorrect data may lead to wrong results of computations instead of errors or warnings. + TESTS:: + + sage: var('x', 'y', 'v', 'w') + (x, y, v, w) + sage: EllipticCurve(y^2 + y > x^3 + x - 9) + Traceback (most recent call last): + ... + ValueError: no symbolic relations other than equalities are allowed + sage: E = EllipticCurve(y^2 + y == x^3 + x - 9) + sage: E is EllipticCurve(y^2 + y - ( x^3 + x - 9 )) + True + sage: R. = QQ[] + sage: E is EllipticCurve(y^2 + y - ( x^3 + x - 9 )) + True """ R = None if is_Ring(x): @@ -400,10 +412,13 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True, raise ValueError("First parameter (if present) must be a ring when j is specified") x = coefficients_from_j(j, minimal_twist) - if is_SymbolicEquation(x): + if isinstance(x, Expression) and x.is_relational(): + import operator + if x.operator() != operator.eq: + raise ValueError("no symbolic relations other than equalities are allowed") x = x.lhs() - x.rhs() - if parent(x) is SR: + if isinstance(parent(x), sage.rings.abc.SymbolicRing): x = x._polynomial_(rings.QQ['x', 'y']) if is_MPolynomial(x): From c9861d16af2e435985ab77239f6eb5b0c775fa6b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 10:05:46 -0700 Subject: [PATCH 065/122] src/sage/interfaces/qepcad.py: Remove use of is_SymbolicEquation --- src/sage/interfaces/qepcad.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 29baa767b4e..9317b166608 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -1924,11 +1924,9 @@ def atomic(self, lhs, op='=', rhs=0): if isinstance(lhs, qformula): return lhs - from sage.symbolic.expression import is_SymbolicEquation - if is_SymbolicEquation(lhs): - rhs = lhs.rhs() - op = lhs.operator() - lhs = lhs.lhs() + from sage.structure.element import Expression + if isinstance(lhs, Expression) and lhs.is_relational(): + lhs, op, rhs = lhs.lhs(), lhs.operator(), lhs.rhs() op = self._normalize_op(op) From 341337a0089d9f27f759f2c5a3f5b33ee795a75c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 10:08:01 -0700 Subject: [PATCH 066/122] src/sage/ext/fast_callable.pyx: Remove use of is_SymbolicVariable --- src/sage/ext/fast_callable.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index da1ad9ed5b3..10acc5e3649 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -438,7 +438,6 @@ def fast_callable(x, domain=None, vars=None, if expect_one_var and len(vars) != 1: raise ValueError(f"passed expect_one_var=True, but the callable expression takes {len(vars)} arguments") elif isinstance(x, Expression_abc): - from sage.symbolic.ring import is_SymbolicVariable if vars is None: vars = x.variables() if expect_one_var and len(vars) <= 1: @@ -447,7 +446,7 @@ def fast_callable(x, domain=None, vars=None, else: raise ValueError("list of variables must be specified for symbolic expressions") def to_var(var): - if is_SymbolicVariable(var): + if isinstance(var, Expression_abc) and var.is_symbol(): return var from sage.symbolic.ring import SR return SR.var(var) From 1635bd3713a426939202e098e515ed5258485ab5 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Sun, 17 Oct 2021 21:09:27 +0200 Subject: [PATCH 067/122] Remove import of Chart in sage.tensor.modules (#32708) --- .../differentiable/tensorfield_paral.py | 32 +++++++++++++++++++ src/sage/tensor/modules/free_module_tensor.py | 11 ------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/sage/manifolds/differentiable/tensorfield_paral.py b/src/sage/manifolds/differentiable/tensorfield_paral.py index 17220bc4fb8..6bcd68becb6 100644 --- a/src/sage/manifolds/differentiable/tensorfield_paral.py +++ b/src/sage/manifolds/differentiable/tensorfield_paral.py @@ -304,6 +304,7 @@ # ***************************************************************************** from sage.tensor.modules.free_module_tensor import FreeModuleTensor +from sage.manifolds.chart import Chart from sage.manifolds.differentiable.tensorfield import TensorField from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism @@ -727,6 +728,37 @@ def _del_derived(self, del_restrictions=True): if del_restrictions: self._del_restrictions() + def _preparse_display(self, basis=None, format_spec=None): + r""" + Helper function, to be used by FreeModuleTensor.display. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: t = M.tensor_field(1, 1) + sage: t._preparse_display() + (Coordinate frame (M, (∂/∂x,∂/∂y)), None) + sage: t._preparse_display(X.frame()) + (Coordinate frame (M, (∂/∂x,∂/∂y)), None) + sage: t._preparse_display(X.frame(), X) + (Coordinate frame (M, (∂/∂x,∂/∂y)), Chart (M, (x, y))) + sage: t._preparse_display(X) # passing a chart instead of a frame + (Coordinate frame (M, (∂/∂x,∂/∂y)), Chart (M, (x, y))) + + """ + if basis is None: + basis = self._fmodule._def_basis + elif isinstance(basis, Chart): + # a coordinate chart has been passed instead of a vector frame; + # the frame is then assumed to be the coordinate frame + # associated to the chart: + if format_spec is None: + format_spec = basis + basis = basis.frame() + return (basis, format_spec) + + def _set_comp_unsafe(self, basis=None): r""" Return the components of the tensor field in a given vector frame diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 47667bfb98c..7515fc080ce 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -200,10 +200,6 @@ class being: from sage.tensor.modules.tensor_with_indices import TensorWithIndices from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism -from sage.manifolds.chart import Chart - -# TODO: remove the import of Chart after _preparse_display has been redefined -# in tensor fields class FreeModuleTensor(ModuleElementWithMutability): r""" @@ -583,13 +579,6 @@ def _preparse_display(self, basis=None, format_spec=None): """ if basis is None: basis = self._fmodule._def_basis - elif isinstance(basis, Chart): - # a coordinate chart has been passed instead of a basis; - # the basis is then assumed to be the coordinate frame - # associated to the chart: - if format_spec is None: - format_spec = basis - basis = basis.frame() return (basis, format_spec) def display(self, basis=None, format_spec=None): From d4357816318a05645edfd169c6f2b08108074fb1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Oct 2021 20:51:48 -0700 Subject: [PATCH 068/122] build/bin/sage-dist-helpers (sdh_store_and_pip_install_wheel): Use sage-pip-uninstall --- build/bin/sage-dist-helpers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index a9639b26871..9980b4e22d2 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -313,7 +313,7 @@ sdh_store_and_pip_install_wheel() { # much -- it also reinstalls all dependencies, which we do not want. wheel_basename="${wheel##*/}" distname="${wheel_basename%%-*}" - $sudo python3 -m pip uninstall --isolated --disable-pip-version-check --yes --no-input $distname + $sudo sage-pip-uninstall "$distname" if [ $? -ne 0 ]; then echo "(ignoring error)" >&2 fi From e7009bd5adf33bf2647b8322360368c6592be922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 18 Oct 2021 18:01:36 +0200 Subject: [PATCH 069/122] adding conf.py in italian tutorial --- src/doc/it/tutorial/conf.py | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/doc/it/tutorial/conf.py diff --git a/src/doc/it/tutorial/conf.py b/src/doc/it/tutorial/conf.py new file mode 100644 index 00000000000..a69378db34f --- /dev/null +++ b/src/doc/it/tutorial/conf.py @@ -0,0 +1,39 @@ +# Sage documentation build configuration file, based on that created by +# sphinx-quickstart on Thu Aug 21 20:15:55 2008. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +from sage.docs.conf import release, latex_elements +from sage.docs.conf import * # NOQA + +# Add any paths that contain custom static files (such as style sheets), +# relative to this directory to html_static_path. They are copied after the +# builtin static files, so a file named "default.css" will overwrite the +# builtin "default.css". html_common_static_path imported from sage.docs.conf +# contains common paths. +html_static_path = [] + html_common_static_path + +# General information about the project. +project = "Tutorial Sage" +name = 'tutorial-it' +language = "it" + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = project + " v" + release + +# Output file base name for HTML help builder. +htmlhelp_basename = name + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +latex_documents = [ + ('index', name + '.tex', project, + 'The Sage Group', 'manual'), +] From c3ff5143e5d7f5b617a7678c43f4b23e7deaed06 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Mon, 18 Oct 2021 22:42:13 +0200 Subject: [PATCH 070/122] Make SR doctests optional in sage.tensor.modules (#32712) --- src/sage/tensor/modules/comp.py | 6 +++--- src/sage/tensor/modules/free_module_alt_form.py | 10 +++++----- src/sage/tensor/modules/free_module_tensor.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 3d775e61850..5b7eef7dc25 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -1168,9 +1168,9 @@ def display(self, symbol, latex_symbol=None, index_positions=None, Check that the bug reported in :trac:`22520` is fixed:: - sage: c = Components(SR, [1, 2], 1) - sage: c[0] = SR.var('t', domain='real') - sage: c.display('c') + sage: c = Components(SR, [1, 2], 1) # optional - sage.symbolic + sage: c[0] = SR.var('t', domain='real') # optional - sage.symbolic + sage: c.display('c') # optional - sage.symbolic c_0 = t """ diff --git a/src/sage/tensor/modules/free_module_alt_form.py b/src/sage/tensor/modules/free_module_alt_form.py index 3e4883ca398..aae1ae990c3 100644 --- a/src/sage/tensor/modules/free_module_alt_form.py +++ b/src/sage/tensor/modules/free_module_alt_form.py @@ -554,11 +554,11 @@ def display(self, basis=None, format_spec=None): Check that the bug reported in :trac:`22520` is fixed:: - sage: M = FiniteRankFreeModule(SR, 2, name='M') - sage: e = M.basis('e') - sage: a = M.alternating_form(2) - sage: a[0,1] = SR.var('t', domain='real') - sage: a.display() + sage: M = FiniteRankFreeModule(SR, 2, name='M') # optional - sage.symbolic + sage: e = M.basis('e') # optional - sage.symbolic + sage: a = M.alternating_form(2) # optional - sage.symbolic + sage: a[0,1] = SR.var('t', domain='real') # optional - sage.symbolic + sage: a.display() # optional - sage.symbolic t e^0∧e^1 """ diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 7515fc080ce..ad17b6018b5 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -687,10 +687,10 @@ def display(self, basis=None, format_spec=None): Check that the bug reported in :trac:`22520` is fixed:: - sage: M = FiniteRankFreeModule(SR, 3, name='M') - sage: e = M.basis('e') - sage: t = SR.var('t', domain='real') - sage: (t*e[0]).display() + sage: M = FiniteRankFreeModule(SR, 3, name='M') # optional - sage.symbolic + sage: e = M.basis('e') # optional - sage.symbolic + sage: t = SR.var('t', domain='real') # optional - sage.symbolic + sage: (t*e[0]).display() # optional - sage.symbolic t e_0 """ From 16a360e2d5dbc29008f19acabd9f2003985558f0 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 18 Aug 2021 18:34:27 -0400 Subject: [PATCH 071/122] Trac #29024: put the shared library extension in sage_conf. Preparing for a system copy of Singular, we need to be able to find the name of the main shared library that Singular installs. In doing so, we avoid the need to guess in _get_shared_lib_path(). In general, the name of this library will be libSingular.$SHLIBEXT, where $SHLIBEXT represents the extension of shared libraries on the system. As such, it makes sense to factor out the "name of the library" into two parts: its base name, and $SHLIBEXT, which can be determined independently and can be used with other libraries. This commit uses an autoconf macro from gettext to determine the shared library extension, and substitutes it into sage_conf.py as SHLIBEXT. --- configure.ac | 8 ++++++++ pkgs/sage-conf/sage_conf.py.in | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/configure.ac b/configure.ac index 0e092a93853..92740cc94d9 100644 --- a/configure.ac +++ b/configure.ac @@ -49,6 +49,14 @@ dnl Make sure the path to our own m4 macros is always properly set dnl and doesn't depend on how autoconf is called. AC_CONFIG_MACRO_DIR([m4]) +dnl The AC_LIB_RPATH macro comes from gettext, which is one of our bootstrap +dnl packages. It defines, among other things, the $acl_shlibext variable that +dnl contains the shared library extension for this system. We already use the +dnl AM_ICONV macro from gettext (which ultimately calls AC_LIB_RPATH), and we +dnl avoid involving libtool by using it to get the shared library extension. +AC_LIB_RPATH +AC_SUBST(SHLIBEXT, "${acl_shlibext}") + #--------------------------------------------------------- # # This is essentially the configure part of the old "install" file. diff --git a/pkgs/sage-conf/sage_conf.py.in b/pkgs/sage-conf/sage_conf.py.in index 86375d2f286..2d5f5fd3cf7 100644 --- a/pkgs/sage-conf/sage_conf.py.in +++ b/pkgs/sage-conf/sage_conf.py.in @@ -9,6 +9,10 @@ VERSION = "@PACKAGE_VERSION@" SAGE_LOCAL = "@prefix@" SAGE_ROOT = "@SAGE_ROOT@" +# The extension (without the period) for shared libraries on this +# system. For example, this is usually "so" on Linux. +SHLIBEXT = "@SHLIBEXT@" + MAXIMA = "@prefix@/bin/maxima" # Delete this line if your ECL can load maxima without further prodding. From 29f44e1381e0819257f8f968742017ea6d0f06e2 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 18 Jul 2021 19:19:57 -0400 Subject: [PATCH 072/122] Trac #29024: new spkg-configure.m4 for singular. This new spkg-configure.m4 tries to use libSingular the way that we do in sage/libs/singular/singular.pyx: by dlopen()ing the library with some flags set. If we can guess the library's name, and if dlopen() works, and if Singular is new enough... we'll accept the system copy, and write the basename of the library into sage_conf. Otherwise, we install the SPKG and write the old SAGE_LOCAL name into sage_conf. The sage_conf variable will be used in a later commit to replace the _get_shared_lib_path() hack currently in use. --- build/pkgs/singular/spkg-configure.m4 | 50 +++++++++++++++++++++++++++ pkgs/sage-conf/sage_conf.py.in | 6 ++++ 2 files changed, 56 insertions(+) create mode 100644 build/pkgs/singular/spkg-configure.m4 diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 new file mode 100644 index 00000000000..37621d817fe --- /dev/null +++ b/build/pkgs/singular/spkg-configure.m4 @@ -0,0 +1,50 @@ +SAGE_SPKG_CONFIGURE([singular], [ + SAGE_SPKG_DEPCHECK([gmp mpir ntl flint readline mpfr cddlib], [ + + AC_PATH_PROG([SINGULAR_BIN], [Singular]) + AS_IF([test -z "${SINGULAR_BIN}"], [sage_spkg_install_singular=yes]) + + dnl Use pkg-config to ensure that Singular is new enough. + PKG_CHECK_MODULES([SINGULAR], + [Singular >= 4.1.1], + [], + [sage_spkg_install_singular=yes]) + + dnl The acl_shlibext variable is set in the top-level configure.ac, + dnl and is ultimately substituted into sage_conf as SHLIBEXT. + SINGULAR_LIB_BASE="libSingular" + SINGULAR_LIB_FILE="${SINGULAR_LIB_BASE}.${acl_shlibext}" + + AC_MSG_CHECKING([if we can dlopen($SINGULAR_LIB_FILE)]) + ORIG_LIBS="${LIBS}" + LIBS="${LIBS} -ldl" + AC_LANG_PUSH(C) + + dnl if we can dlopen() it, substitute the name for sage_conf; + dnl otherwise, fall back to using the SPKG. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[void* h = dlopen("${SINGULAR_LIB_FILE}", RTLD_LAZY | RTLD_GLOBAL); + if (h == 0) { return 1; } else { return dlclose(h); }]] + )], + [AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}")], + [sage_spkg_install_singular=yes] + ) + + AC_LANG_POP() + LIBS="${ORIG_LIBS}" + ]) +],[],[],[ + dnl Post-check phase + dnl We make the sage_conf substitutions here, because the "default" + dnl substitution needs to be made even if we skipped the system-Singular + dnl checks themselves. + + dnl If we're using the SPKG, we might as well use the FULL path to the + dnl library, because we know it. + AS_IF([test "x${sage_spkg_install_singular}" = "xyes"], + [AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/lib/libSingular')], + [AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}")] + ) +]) diff --git a/pkgs/sage-conf/sage_conf.py.in b/pkgs/sage-conf/sage_conf.py.in index 2d5f5fd3cf7..7aa69e92d93 100644 --- a/pkgs/sage-conf/sage_conf.py.in +++ b/pkgs/sage-conf/sage_conf.py.in @@ -59,6 +59,12 @@ THREEJS_DIR = SAGE_LOCAL + "/share/threejs-sage" OPENMP_CFLAGS = "@OPENMP_CFLAGS@" OPENMP_CXXFLAGS = "@OPENMP_CXXFLAGS@" +# The base name of the main Singular library. If the library is called +# libSingular.so, then its base name should be something like "libSingular", +# or "$SAGE_LOCAL/lib/libSingular". The extension is not needed; the value +# of SHLIBEXT will be appended automatically. +SINGULAR_LIB_BASE = "@SINGULAR_LIB_BASE@".replace('$SAGE_LOCAL', SAGE_LOCAL) + # Entry point 'sage-config'. It does not depend on any packages. def _main(): From f2ac75078aec9b01e7d644aff7f000346183a442 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 18 Aug 2021 22:08:41 -0400 Subject: [PATCH 073/122] Trac #29024: don't set SINGULARPATH in sage-env. This was never really necessary (the Singular library/executable knows where its own libs live), and is now wrong when we're using a system copy of Singular. Goodbye. --- src/bin/sage-env | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bin/sage-env b/src/bin/sage-env index ac8506ffba6..b4c0c6ea207 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -334,8 +334,6 @@ if [ -n "$SAGE_LOCAL" ]; then fi if [ -n "$SAGE_LOCAL" ]; then - SINGULARPATH="$SAGE_LOCAL/share/singular" && export SINGULARPATH - # Ensure that there is a colon at the end of $INFOPATH by # stripping the existing one (if it exists), and then adding a new # one. This forces the "info" program to check various default From cbeb23feeb6c336770bdd2a915a3438fa52f4586 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 18 Aug 2021 22:08:49 -0400 Subject: [PATCH 074/122] Trac #29024: use SINGULAR_LIB_BASE and SHLIBEXT to replace SINGULAR_SO. At this point, sage_conf contains both the "base name" of the singular library and the extension used by all shared libraries on this system. Instead of guessing at the path of the Singular library through _get_shared_lib_path() in sage.env, this commit updates the code in sage.libs.singular.singular to use the new sage_conf variables. All mentions of the earlier variable (SINGULAR_SO) have been deleted. --- src/sage/env.py | 4 ---- src/sage/libs/singular/singular.pyx | 12 ++++-------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/sage/env.py b/src/sage/env.py index 44fc8481346..876c56d742d 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -316,10 +316,6 @@ def _get_shared_lib_path(*libnames: str) -> Optional[str]: # Just return None if no files were found return None -# locate singular shared object -# On Debian it's libsingular-Singular so try that as well -SINGULAR_SO = var("SINGULAR_SO", _get_shared_lib_path("Singular", "singular-Singular")) - # locate libgap shared object GAP_SO = var("GAP_SO", _get_shared_lib_path("gap", "")) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index c736d56b0c0..3e47c62db3b 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -768,18 +768,14 @@ cdef init_libsingular(): cdef void *handle = NULL - from sage.env import SINGULAR_SO - if not SINGULAR_SO or not os.path.exists(SINGULAR_SO): - raise RuntimeError( - "libSingular not found--a working Singular install " - "is required for Sage to work") - - lib = str_to_bytes(SINGULAR_SO, FS_ENCODING, "surrogateescape") + from sage_conf import SINGULAR_LIB_BASE, SHLIBEXT + SINGULAR_LIB_PATH = SINGULAR_LIB_BASE + "." + SHLIBEXT + lib = str_to_bytes(SINGULAR_LIB_PATH, FS_ENCODING, "surrogateescape") handle = dlopen(lib, RTLD_GLOBAL|RTLD_LAZY) if not handle: err = dlerror() - raise ImportError(f"cannot load Singular library from {SINGULAR_SO} ({err})") + raise ImportError(f"cannot load Singular library from {SINGULAR_LIB_PATH} ({err})") # load SINGULAR siInit(lib) From dfb586073a5d4cc288ef3016906f380de1330411 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Aug 2021 09:11:02 -0400 Subject: [PATCH 075/122] Trac #29024: add Gentoo package information. --- build/pkgs/singular/distros/gentoo.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/singular/distros/gentoo.txt diff --git a/build/pkgs/singular/distros/gentoo.txt b/build/pkgs/singular/distros/gentoo.txt new file mode 100644 index 00000000000..2c8b3567a39 --- /dev/null +++ b/build/pkgs/singular/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/singular[readline] From 175e6f60242d254cd4b740f35a8a4f0279aec40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sat, 21 Aug 2021 19:55:17 +0200 Subject: [PATCH 076/122] 29024: Add Singular distro info --- build/pkgs/singular/distros/arch.txt | 1 + build/pkgs/singular/distros/cygwin.txt | 2 ++ build/pkgs/singular/distros/fedora.txt | 1 + build/pkgs/singular/distros/homebrew.txt | 5 ++++- 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/singular/distros/arch.txt create mode 100644 build/pkgs/singular/distros/cygwin.txt create mode 100644 build/pkgs/singular/distros/fedora.txt diff --git a/build/pkgs/singular/distros/arch.txt b/build/pkgs/singular/distros/arch.txt new file mode 100644 index 00000000000..5f0dc01955f --- /dev/null +++ b/build/pkgs/singular/distros/arch.txt @@ -0,0 +1 @@ +singular diff --git a/build/pkgs/singular/distros/cygwin.txt b/build/pkgs/singular/distros/cygwin.txt new file mode 100644 index 00000000000..774625930f9 --- /dev/null +++ b/build/pkgs/singular/distros/cygwin.txt @@ -0,0 +1,2 @@ +singular-devel +singular diff --git a/build/pkgs/singular/distros/fedora.txt b/build/pkgs/singular/distros/fedora.txt new file mode 100644 index 00000000000..b703b849699 --- /dev/null +++ b/build/pkgs/singular/distros/fedora.txt @@ -0,0 +1 @@ +Singular diff --git a/build/pkgs/singular/distros/homebrew.txt b/build/pkgs/singular/distros/homebrew.txt index 90958411e8f..ef2339d4ab5 100644 --- a/build/pkgs/singular/distros/homebrew.txt +++ b/build/pkgs/singular/distros/homebrew.txt @@ -1,2 +1,5 @@ -# As of 2021-03-20, build of homebrew's singular from source fails +# [2021-03-20] Homebrew's Singular can be built from source +# (via `brew install -s`, or implied when using `brew` with +# a non-standard prefix), but that currently fails. + #singular From dc1dcc336664e40dfda0015e19ff2999f76d1bd4 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 14 Sep 2021 12:43:44 -0400 Subject: [PATCH 077/122] Trac #29024: work around Singular issue 1113 in init_libsingular(). When we Singular with dlopen() by name, it is initially unable to determine the path to its main executable and emits a "BUG" warning. This is ultimately harmless, but is very scary and appears unexpectedly whenever Sage is invoked. Here we work around the issue temporarily by setting SINGULAR_BIN_DIR to the directory containing the "Singular" executable in the user's PATH. --- src/sage/libs/singular/singular.pyx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 3e47c62db3b..f6ebfa01d31 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -772,6 +772,13 @@ cdef init_libsingular(): SINGULAR_LIB_PATH = SINGULAR_LIB_BASE + "." + SHLIBEXT lib = str_to_bytes(SINGULAR_LIB_PATH, FS_ENCODING, "surrogateescape") + # This is a workaround for https://github.com/Singular/Singular/issues/1113 + # and can be removed once that fix makes it into release of Singular that + # is supported by sage. + from shutil import which + from os.path import dirname + os.environ["SINGULAR_BIN_DIR"] = dirname(which("Singular")) + handle = dlopen(lib, RTLD_GLOBAL|RTLD_LAZY) if not handle: err = dlerror() From ef79af8694275ed26bab03830e6bccb2cd5c2e13 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 17 Sep 2021 23:35:12 -0400 Subject: [PATCH 078/122] Trac #29024: add missing AC_MSG_RESULTs in singular/spkg-configure.m4. --- build/pkgs/singular/spkg-configure.m4 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index 37621d817fe..02bdc2d0c42 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -27,10 +27,13 @@ SAGE_SPKG_CONFIGURE([singular], [ [[#include ]], [[void* h = dlopen("${SINGULAR_LIB_FILE}", RTLD_LAZY | RTLD_GLOBAL); if (h == 0) { return 1; } else { return dlclose(h); }]] - )], - [AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}")], - [sage_spkg_install_singular=yes] - ) + )], [ + AC_MSG_RESULT(yes) + AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}") + ], [ + AC_MSG_RESULT(no) + sage_spkg_install_singular=yes + ]) AC_LANG_POP() LIBS="${ORIG_LIBS}" From 2f32b6920d4a029d94af9e7ede5fa91bbcff612e Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 19 Sep 2021 21:43:11 -0400 Subject: [PATCH 079/122] Trac #29024: clarify a comment in singular's spkg-configure. --- build/pkgs/singular/spkg-configure.m4 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index 02bdc2d0c42..85bd033c6a4 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -44,8 +44,10 @@ SAGE_SPKG_CONFIGURE([singular], [ dnl substitution needs to be made even if we skipped the system-Singular dnl checks themselves. - dnl If we're using the SPKG, we might as well use the FULL path to the - dnl library, because we know it. + dnl If we're using the SPKG, we need to use the FULL path to the library, + dnl because the use of the SPKG is not just a fallback for when no system + dnl singular is present; it is also a fallback for when our (non-POSIX) + dnl dlopen() strategy fails. AS_IF([test "x${sage_spkg_install_singular}" = "xyes"], [AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/lib/libSingular')], [AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}")] From 8ec888adb74c05a078c525cf382f9a1b4bca59ca Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 20 Sep 2021 10:47:28 -0400 Subject: [PATCH 080/122] Trac #29024: fix libSingular path on Cygwin. On Cygwin, libtool.m4 uses postinstall_cmds to move DLLs out of $libdir and into $libdir/../bin. To account for that, we add a special case to Singular's spkg-configure.m4 that uses $libdir/../bin instead of $libdir as the location for libSingular on Cygwin. --- build/pkgs/singular/spkg-configure.m4 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index 85bd033c6a4..39fbda32052 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -48,8 +48,19 @@ SAGE_SPKG_CONFIGURE([singular], [ dnl because the use of the SPKG is not just a fallback for when no system dnl singular is present; it is also a fallback for when our (non-POSIX) dnl dlopen() strategy fails. - AS_IF([test "x${sage_spkg_install_singular}" = "xyes"], - [AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/lib/libSingular')], - [AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}")] + AS_IF([test "x${sage_spkg_install_singular}" = "xyes"], [ + AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/lib/libSingular') + + dnl libtool.m4 has dedicated cygwin* code to move DLLs from + dnl $libdir to $libdir/../bin. + AS_CASE([$host_os], + [cygwin*], [ + AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/bin/libSingular') + ] + ) + ], [ + dnl We're using the system package + AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}") + ] ) ]) From 62065de4b44c87638b561cd7ce8b8bbc19adc6ab Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 20 Sep 2021 14:50:57 -0400 Subject: [PATCH 081/122] Trac #29024: always obtain the absolute path to libSingular. Trusting dlopen() to look up its argument in the system's library search path did not work as well as expected. At least on macOS -- and probably elsewhere -- the autoconf test succeeds but the real call in singular.pyx fails, requiring us to pass it the absolute path instead. That path can be obtained from pkg-config. We're already using pkg-config for a version check, and the name-only behavior of dlopen() is not standard, so relying on pkg-config to get singular's libdir does not seem so bad in this case. To simplify the implementation, this commit uses the libdir obtained from pkg-config unconditionally, and not just on macOS. It also adds some (untested) code in preparation for a system copy of Singular on Cygwin. --- build/pkgs/singular/spkg-configure.m4 | 42 +++++++++++++++------------ pkgs/sage-conf/sage_conf.py.in | 11 ++----- src/sage/libs/singular/singular.pyx | 7 ++--- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index 39fbda32052..eae8d9df35f 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -10,12 +10,24 @@ SAGE_SPKG_CONFIGURE([singular], [ [], [sage_spkg_install_singular=yes]) - dnl The acl_shlibext variable is set in the top-level configure.ac, - dnl and is ultimately substituted into sage_conf as SHLIBEXT. - SINGULAR_LIB_BASE="libSingular" - SINGULAR_LIB_FILE="${SINGULAR_LIB_BASE}.${acl_shlibext}" + dnl Use pkg-config to get singular's libdir while we're at it. As a + dnl moral compromise for using pkg-config, this ultimately allows us + dnl to pass an absolute path to dlopen(), which is the only approach + dnl that POSIX guarantees will work. + PKG_CHECK_VAR([SINGULAR_LIB_DIR], [Singular], [libdir]) - AC_MSG_CHECKING([if we can dlopen($SINGULAR_LIB_FILE)]) + dnl libtool.m4 has dedicated cygwin* code to move DLLs from + dnl $libdir to $libdir/../bin. + AS_CASE([$host_os], + [cygwin*], [ + SINGULAR_LIB_DIR="${SINGULAR_LIB_DIR}/../bin" + ] + ) + + dnl The acl_shlibext variable is set in the top-level configure.ac. + LIBSINGULAR_PATH="${SINGULAR_LIB_DIR}/libSingular.${acl_shlibext}" + + AC_MSG_CHECKING([if we can dlopen($LIBSINGULAR_PATH)]) ORIG_LIBS="${LIBS}" LIBS="${LIBS} -ldl" AC_LANG_PUSH(C) @@ -25,11 +37,10 @@ SAGE_SPKG_CONFIGURE([singular], [ AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[#include ]], - [[void* h = dlopen("${SINGULAR_LIB_FILE}", RTLD_LAZY | RTLD_GLOBAL); + [[void* h = dlopen("${LIBSINGULAR_PATH}", RTLD_LAZY | RTLD_GLOBAL); if (h == 0) { return 1; } else { return dlclose(h); }]] )], [ AC_MSG_RESULT(yes) - AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}") ], [ AC_MSG_RESULT(no) sage_spkg_install_singular=yes @@ -43,24 +54,17 @@ SAGE_SPKG_CONFIGURE([singular], [ dnl We make the sage_conf substitutions here, because the "default" dnl substitution needs to be made even if we skipped the system-Singular dnl checks themselves. - - dnl If we're using the SPKG, we need to use the FULL path to the library, - dnl because the use of the SPKG is not just a fallback for when no system - dnl singular is present; it is also a fallback for when our (non-POSIX) - dnl dlopen() strategy fails. AS_IF([test "x${sage_spkg_install_singular}" = "xyes"], [ - AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/lib/libSingular') + LIBSINGULAR_PATH="\$SAGE_LOCAL/lib/libSingular.${acl_shlibext}" dnl libtool.m4 has dedicated cygwin* code to move DLLs from dnl $libdir to $libdir/../bin. AS_CASE([$host_os], [cygwin*], [ - AC_SUBST(SINGULAR_LIB_BASE, '$SAGE_LOCAL/bin/libSingular') + LIBSINGULAR_PATH="\$SAGE_LOCAL/bin/libSingular.${acl_shlibext}" ] ) - ], [ - dnl We're using the system package - AC_SUBST(SINGULAR_LIB_BASE, "${SINGULAR_LIB_BASE}") - ] - ) + ]) + + AC_SUBST(LIBSINGULAR_PATH, "${LIBSINGULAR_PATH}") ]) diff --git a/pkgs/sage-conf/sage_conf.py.in b/pkgs/sage-conf/sage_conf.py.in index 7aa69e92d93..fbce6947de3 100644 --- a/pkgs/sage-conf/sage_conf.py.in +++ b/pkgs/sage-conf/sage_conf.py.in @@ -9,10 +9,6 @@ VERSION = "@PACKAGE_VERSION@" SAGE_LOCAL = "@prefix@" SAGE_ROOT = "@SAGE_ROOT@" -# The extension (without the period) for shared libraries on this -# system. For example, this is usually "so" on Linux. -SHLIBEXT = "@SHLIBEXT@" - MAXIMA = "@prefix@/bin/maxima" # Delete this line if your ECL can load maxima without further prodding. @@ -59,11 +55,8 @@ THREEJS_DIR = SAGE_LOCAL + "/share/threejs-sage" OPENMP_CFLAGS = "@OPENMP_CFLAGS@" OPENMP_CXXFLAGS = "@OPENMP_CXXFLAGS@" -# The base name of the main Singular library. If the library is called -# libSingular.so, then its base name should be something like "libSingular", -# or "$SAGE_LOCAL/lib/libSingular". The extension is not needed; the value -# of SHLIBEXT will be appended automatically. -SINGULAR_LIB_BASE = "@SINGULAR_LIB_BASE@".replace('$SAGE_LOCAL', SAGE_LOCAL) +# The full absolute path to the main Singular library. +LIBSINGULAR_PATH = "@LIBSINGULAR_PATH@".replace('$SAGE_LOCAL', SAGE_LOCAL) # Entry point 'sage-config'. It does not depend on any packages. diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index f6ebfa01d31..ea6d0ab01d1 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -768,9 +768,8 @@ cdef init_libsingular(): cdef void *handle = NULL - from sage_conf import SINGULAR_LIB_BASE, SHLIBEXT - SINGULAR_LIB_PATH = SINGULAR_LIB_BASE + "." + SHLIBEXT - lib = str_to_bytes(SINGULAR_LIB_PATH, FS_ENCODING, "surrogateescape") + from sage_conf import LIBSINGULAR_PATH + lib = str_to_bytes(LIBSINGULAR_PATH, FS_ENCODING, "surrogateescape") # This is a workaround for https://github.com/Singular/Singular/issues/1113 # and can be removed once that fix makes it into release of Singular that @@ -782,7 +781,7 @@ cdef init_libsingular(): handle = dlopen(lib, RTLD_GLOBAL|RTLD_LAZY) if not handle: err = dlerror() - raise ImportError(f"cannot load Singular library from {SINGULAR_LIB_PATH} ({err})") + raise ImportError(f"cannot load Singular library from {LIBSINGULAR_PATH} ({err})") # load SINGULAR siInit(lib) From 2aa55e7fb03a6c7ad8db80c779c9fb70b17d8290 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 18 Oct 2021 20:47:37 -0400 Subject: [PATCH 082/122] Trac #29024: fully nest tests in Singular's spkg-configure.m4. This should avoid performing unnecessary checks later in the macro after an earlier check has already failed. --- build/pkgs/singular/spkg-configure.m4 | 75 ++++++++++++++------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index eae8d9df35f..1eea02e9925 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -2,52 +2,53 @@ SAGE_SPKG_CONFIGURE([singular], [ SAGE_SPKG_DEPCHECK([gmp mpir ntl flint readline mpfr cddlib], [ AC_PATH_PROG([SINGULAR_BIN], [Singular]) - AS_IF([test -z "${SINGULAR_BIN}"], [sage_spkg_install_singular=yes]) + AS_IF([test -z "${SINGULAR_BIN}"], [sage_spkg_install_singular=yes], [ + dnl Use pkg-config to ensure that Singular is new enough. + PKG_CHECK_MODULES([SINGULAR], [Singular >= 4.1.1], [ - dnl Use pkg-config to ensure that Singular is new enough. - PKG_CHECK_MODULES([SINGULAR], - [Singular >= 4.1.1], - [], - [sage_spkg_install_singular=yes]) + dnl Use pkg-config to get singular's libdir while we're at it. As a + dnl moral compromise for using pkg-config, this ultimately allows us + dnl to pass an absolute path to dlopen(), which is the only approach + dnl that POSIX guarantees will work. + PKG_CHECK_VAR([SINGULAR_LIB_DIR], [Singular], [libdir]) - dnl Use pkg-config to get singular's libdir while we're at it. As a - dnl moral compromise for using pkg-config, this ultimately allows us - dnl to pass an absolute path to dlopen(), which is the only approach - dnl that POSIX guarantees will work. - PKG_CHECK_VAR([SINGULAR_LIB_DIR], [Singular], [libdir]) + dnl libtool.m4 has dedicated cygwin* code to move DLLs from + dnl $libdir to $libdir/../bin. + AS_CASE([$host_os], + [cygwin*], [ + SINGULAR_LIB_DIR="${SINGULAR_LIB_DIR}/../bin" + ] + ) - dnl libtool.m4 has dedicated cygwin* code to move DLLs from - dnl $libdir to $libdir/../bin. - AS_CASE([$host_os], - [cygwin*], [ - SINGULAR_LIB_DIR="${SINGULAR_LIB_DIR}/../bin" - ] - ) + dnl The acl_shlibext variable is set in the top-level configure.ac. + LIBSINGULAR_PATH="${SINGULAR_LIB_DIR}/libSingular.${acl_shlibext}" - dnl The acl_shlibext variable is set in the top-level configure.ac. - LIBSINGULAR_PATH="${SINGULAR_LIB_DIR}/libSingular.${acl_shlibext}" + AC_MSG_CHECKING([if we can dlopen($LIBSINGULAR_PATH)]) + ORIG_LIBS="${LIBS}" + LIBS="${LIBS} -ldl" + AC_LANG_PUSH(C) - AC_MSG_CHECKING([if we can dlopen($LIBSINGULAR_PATH)]) - ORIG_LIBS="${LIBS}" - LIBS="${LIBS} -ldl" - AC_LANG_PUSH(C) + dnl if we can dlopen() it, substitute the name for sage_conf; + dnl otherwise, fall back to using the SPKG. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[void* h = dlopen("${LIBSINGULAR_PATH}", RTLD_LAZY | RTLD_GLOBAL); + if (h == 0) { return 1; } else { return dlclose(h); }]] + )], [ + AC_MSG_RESULT(yes) + ], [ + AC_MSG_RESULT(no) + sage_spkg_install_singular=yes + ]) - dnl if we can dlopen() it, substitute the name for sage_conf; - dnl otherwise, fall back to using the SPKG. - AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [[#include ]], - [[void* h = dlopen("${LIBSINGULAR_PATH}", RTLD_LAZY | RTLD_GLOBAL); - if (h == 0) { return 1; } else { return dlclose(h); }]] - )], [ - AC_MSG_RESULT(yes) + AC_LANG_POP() + LIBS="${ORIG_LIBS}" ], [ - AC_MSG_RESULT(no) + dnl pkg-config version check failed sage_spkg_install_singular=yes + ]) ]) - - AC_LANG_POP() - LIBS="${ORIG_LIBS}" ]) ],[],[],[ dnl Post-check phase From eecd43c457228ef2b05832ba11d40bc17127e5f4 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 21 Sep 2021 20:03:47 +0100 Subject: [PATCH 083/122] Fedora needs devel pkg --- build/pkgs/singular/distros/fedora.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/singular/distros/fedora.txt b/build/pkgs/singular/distros/fedora.txt index b703b849699..bb4a7a5c7bf 100644 --- a/build/pkgs/singular/distros/fedora.txt +++ b/build/pkgs/singular/distros/fedora.txt @@ -1 +1,2 @@ Singular +Singular-devel From eb3b4e559096605857584054ecdfe3556e6444ed Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 18 Oct 2021 21:26:40 -0400 Subject: [PATCH 084/122] Trac #29024: drop "mpir" from singular's DEPCHECK. Our mpir package was removed in trac 32549. --- build/pkgs/singular/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/singular/spkg-configure.m4 b/build/pkgs/singular/spkg-configure.m4 index 1eea02e9925..1426d49f04e 100644 --- a/build/pkgs/singular/spkg-configure.m4 +++ b/build/pkgs/singular/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([singular], [ - SAGE_SPKG_DEPCHECK([gmp mpir ntl flint readline mpfr cddlib], [ + SAGE_SPKG_DEPCHECK([gmp ntl flint readline mpfr cddlib], [ AC_PATH_PROG([SINGULAR_BIN], [Singular]) AS_IF([test -z "${SINGULAR_BIN}"], [sage_spkg_install_singular=yes], [ From 8eda301553e82ec2aa95c61b535f540cff5ebb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Oct 2021 08:59:42 +0200 Subject: [PATCH 085/122] some fixes in it tutorial --- src/doc/it/tutorial/tour_algebra.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/doc/it/tutorial/tour_algebra.rst b/src/doc/it/tutorial/tour_algebra.rst index c745905ab05..cc331c5f427 100644 --- a/src/doc/it/tutorial/tour_algebra.rst +++ b/src/doc/it/tutorial/tour_algebra.rst @@ -1,5 +1,4 @@ Algebra di base e Analisi - ========================= Sage sa svolgere diversi calcoli legati all'algebra di base @@ -156,7 +155,7 @@ Si può anche calcolare la trasformata di Laplace; la trasformata di Laplace di Il successivo è un esempio più articolato. Lo scostamento dall'equilibrio (rispettivamente) per due molle accoppiate fissate ad un muro a sinistra -:: +.. CODE-BLOCK:: text |------\/\/\/\/\---|massa1|----\/\/\/\/\/----|massa2| molla1 molla2 @@ -164,11 +163,11 @@ Il successivo è un esempio più articolato. Lo scostamento dall'equilibrio è modellizzato dal sistema di equazioni differenziali del secondo ordine .. math:: + m_1 x_1'' + (k_1+k_2) x_1 - k_2 x_2 = 0 m_2 x_2''+ k_2 (x_2-x_1) = 0, - dove :math:`m_{i}` è la massa dell'oggetto *i*, :math:`x_{i}` è lo scostamento dall'equilibrio della massa *i*, e :math:`k_{i}` è la costante elastica della molla *i*. @@ -245,9 +244,7 @@ Essa può essere disegnata in forma parametrica usando ....: (0, 2*pi), rgbcolor=hue(0.9)) sage: show(P) -Le singole componenti possono essere tracciate usando: - -:: +Le singole componenti possono essere tracciate usando:: sage: t = var('t') sage: p1 = plot(cos(2*t) + 2*cos(t), 0, 2*pi, rgbcolor=hue(0.3)) From a3c53051a5961226700bdb8a40f831092f8dffcd Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 19 Oct 2021 07:16:55 -0400 Subject: [PATCH 086/122] Trac #29024: re-enable the system singular hint on homebrew. --- build/pkgs/singular/distros/homebrew.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/build/pkgs/singular/distros/homebrew.txt b/build/pkgs/singular/distros/homebrew.txt index ef2339d4ab5..5f0dc01955f 100644 --- a/build/pkgs/singular/distros/homebrew.txt +++ b/build/pkgs/singular/distros/homebrew.txt @@ -1,5 +1 @@ -# [2021-03-20] Homebrew's Singular can be built from source -# (via `brew install -s`, or implied when using `brew` with -# a non-standard prefix), but that currently fails. - -#singular +singular From b9fb2fb5b8dbc3fcce8891877c5d6aa89f71b0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Oct 2021 16:31:44 +0200 Subject: [PATCH 087/122] another fix for it tutorial --- src/doc/it/tutorial/tour_algebra.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/doc/it/tutorial/tour_algebra.rst b/src/doc/it/tutorial/tour_algebra.rst index cc331c5f427..e17a93dde8a 100644 --- a/src/doc/it/tutorial/tour_algebra.rst +++ b/src/doc/it/tutorial/tour_algebra.rst @@ -286,7 +286,7 @@ per :math:`y(x+h)`: Se chiamiamo :math:`h f(x,y(x))` il "termine di correzione" (per mancanza di un termine migliore), :math:`y(x)` il "vecchio valore di *y*", e - :math:`y(x+h)` il "nuovo valore di *y*", allora questa +:math:`y(x+h)` il "nuovo valore di *y*", allora questa approssimazione può essere espressa come .. math:: y_{new} \approx y_{old} + h*f(x,y_{old}). @@ -381,9 +381,7 @@ e "Funzioni speciali", rispettivamente) del manuale di Sage. A questo punto, Sage ha soltanto incorporato queste funzioni per l'uso numerico. Per l'uso simbolico, si usi direttamente l'intefaccia di Maxima, come -nell'esempio seguente: - -:: +nell'esempio seguente:: sage: maxima.eval("f:bessel_y(v, w)") 'bessel_y(v,w)' From e1b526c6be9cfc1bcb879fadbe63fb2d56b66010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Oct 2021 18:23:43 +0200 Subject: [PATCH 088/122] trying to make derivative od piecewise work --- src/sage/functions/piecewise.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index bc25aad5cd1..4ac28309a5d 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -283,6 +283,28 @@ def simplify(ex): """ raise NotImplementedError + def _tderivative_(self, parameters, variable, *args, **kwds): + """ + Return the derivative of the piecewise function by applying the + derivative to each piece. + + EXAMPLES:: + + sage: f = piecewise([ [(-1,1), x**2], [(1,3), x**3]]) + sage: f.diff() + piecewise(x|-->2*x on (-1, 1), x|-->3*x^2 on (1, 3); x) + sage: f.diff(x,x) + piecewise(x|-->2 on (-1, 1), x|-->6*x on (1, 3); x) + + TESTS:: + + sage: f = piecewise([((-oo, -1),0), ((-1, 1),exp(-1/(1 - x^2))), ((1, oo),0)]) + sage: f.diff() + piecewise(x|-->0 on (-oo, -1), x|-->-2*x*e^(1/(x^2 - 1))/(x^2 - 1)^2 on (-1, 1), x|-->0 on (1, +oo); x) + """ + return piecewise([(domain, func.derivative(*args)) + for domain, func in parameters], + var=variable) class EvaluationMethods(object): From 6800b36db28020ac40dcdf3236c661a8db4197ce Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 19 Oct 2021 18:35:32 +0200 Subject: [PATCH 089/122] trac #32723: add method _get_weight_function and show usage --- src/sage/graphs/generic_graph.py | 102 +++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 2ab81f802b2..097d450f5c0 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -16181,6 +16181,81 @@ def _check_weight_function(self, weight_function=None): raise ValueError("the weight function cannot find the " "weight of " + str(e)) + def _get_weight_function(self, by_weight=False, weight_function=None, check_weight=True): + r""" + Return an edge weight function. + + An edge weight function is a function that takes as input an edge and + outputs its weight. + + This method is a helper function for methods using the weight of edges. + It either checks the validity of an input weight function, or returns a + valid weight function on the edges. + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges + in the graph are weighted, otherwise all edges have weight 1 + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the weight_function outputs a number for each edge + + EXAMPLES: + + The default weight function outputs 1 for each edge:: + + sage: G = Graph([(0, 1, 1), (1, 2, 3), (2, 3, 2)]) + sage: by_weight, weight_function = G._get_weight_function() + sage: by_weight + False + sage: [weight_function(e) for e in G.edges()] + [1, 1, 1] + + The standard weight function outputs labels:: + + sage: G = Graph([(0, 1, 1), (1, 2, 3), (2, 3, 2)]) + sage: by_weight, weight_function = G._get_weight_function(by_weight=True) + sage: by_weight + True + sage: [weight_function(e) for e in G.edges()] + [1, 3, 2] + + However, it might be more complicated:: + + sage: G = Graph([(0, 1, {'name':'a', 'weight':1}), (1, 2, {'name': 'b', 'weight': 3}), (2, 3, {'name': 'c', 'weight': 2})]) + sage: by_weight, weight_function = G._get_weight_function(weight_function=lambda e:e[2]['weight']) + sage: by_weight + True + sage: [weight_function(e) for e in G.edges()] + [1, 3, 2] + + Numeric string as a weight in weight_function:: + + sage: G = Graph({0: {1: '123'}}) + sage: by_weight, weight_function = G._get_weight_function(by_weight=True) + Traceback (most recent call last): + ... + ValueError: the weight function cannot find the weight of (0, 1, '123') + """ + if weight_function is not None: + by_weight = True + if by_weight: + if weight_function is None: + def weight_function(e): + return 1 if e[2] is None else e[2] + if check_weight: + self._check_weight_function(weight_function) + else: + def weight_function(e): + return 1 + return by_weight, weight_function + def shortest_paths(self, u, by_weight=False, algorithm=None, weight_function=None, check_weight=True, cutoff=None): r""" @@ -16324,21 +16399,13 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, ... ValueError: ('Contradictory paths found:', 'negative weights?') """ - if weight_function is not None: - by_weight = True - elif by_weight: - def weight_function(e): - return 1 if e[2] is None else e[2] - else: - def weight_function(e): - return 1 + by_weight, weight_function = self._get_weight_function(by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight) if algorithm is None and not by_weight: algorithm = 'BFS' - if by_weight and check_weight: - self._check_weight_function(weight_function) - if algorithm == 'BFS': if by_weight: raise ValueError("the 'BFS' algorithm does not work on " @@ -16440,14 +16507,11 @@ def _path_length(self, path, by_weight=False, weight_function=None): return Infinity if by_weight or weight_function is not None: - if weight_function is None: - def weight_function(e): - return 1 if e[2] is None else e[2] - wt = 0 - - for u, v in zip(path[:-1], path[1:]): - wt += weight_function((u, v, self.edge_label(u, v))) - return wt + _, weight_function = self._get_weight_function(by_weight=by_weight, + weight_function=weight_function, + check_weight=False) + return sum(weight_function((u, v, self.edge_label(u, v))) + for u, v in zip(path[:-1], path[1:])) else: return len(path) - 1 From efdac8754b4b13736d1f9e4be6369ca7536113d8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 19 Oct 2021 21:07:45 -0700 Subject: [PATCH 090/122] build/pkgs/argcomplete: Make it a standard package --- build/pkgs/argcomplete/type | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/argcomplete/type b/build/pkgs/argcomplete/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/argcomplete/type +++ b/build/pkgs/argcomplete/type @@ -1 +1 @@ -optional +standard From aa557a9eaa8c798d3fc4d3269c32edd68aad6ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Oct 2021 09:20:20 +0200 Subject: [PATCH 091/122] one more fix for italian tutorial --- src/doc/it/tutorial/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/it/tutorial/index.rst b/src/doc/it/tutorial/index.rst index c4c4ade017a..4fb1d04c523 100644 --- a/src/doc/it/tutorial/index.rst +++ b/src/doc/it/tutorial/index.rst @@ -29,4 +29,4 @@ Indici e tabelle * :ref:`genindex` * :ref:`modindex` -* :ref:`cerca` +* :ref:`search` From 19442e760215d508656584685940731491f95590 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 20 Oct 2021 17:28:29 +0800 Subject: [PATCH 092/122] deduplicate copies of QuaternionAlgebraElement_abstract.pair() --- .../algebras/quatalg/quaternion_algebra.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 89671f93593..10d4a3166e5 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -61,7 +61,11 @@ from operator import itemgetter -from . import quaternion_algebra_element +from .quaternion_algebra_element import \ + QuaternionAlgebraElement_abstract, \ + QuaternionAlgebraElement_generic, \ + QuaternionAlgebraElement_rational_field, \ + QuaternionAlgebraElement_number_field from . import quaternion_algebra_cython from sage.modular.modsym.p1list import P1List @@ -650,7 +654,7 @@ def __init__(self, base_ring, a, b, names='i,j,k'): self._a = a self._b = b if is_RationalField(base_ring) and a.denominator() == 1 and b.denominator() == 1: - self.Element = quaternion_algebra_element.QuaternionAlgebraElement_rational_field + self.Element = QuaternionAlgebraElement_rational_field elif is_NumberField(base_ring) and base_ring.degree() > 2 and base_ring.is_absolute() and \ a.denominator() == 1 and b.denominator() == 1 and base_ring.defining_polynomial().is_monic(): # This QuaternionAlgebraElement_number_field class is not @@ -660,9 +664,9 @@ def __init__(self, base_ring, a, b, names='i,j,k'): # (or more?) speedup. Much care must be taken because the # underlying representation of quadratic fields is a bit # tricky. - self.Element = quaternion_algebra_element.QuaternionAlgebraElement_number_field + self.Element = QuaternionAlgebraElement_number_field else: - self.Element = quaternion_algebra_element.QuaternionAlgebraElement_generic + self.Element = QuaternionAlgebraElement_generic self._populate_coercion_lists_(coerce_list=[base_ring]) self._gens = [self([0,1,0,0]), self([0,0,1,0]), self([0,0,0,1])] @@ -1666,7 +1670,7 @@ def discriminant(self): for d in self.basis(): MM = [] for e in self.basis(): - MM.append((d * e.conjugate()).reduced_trace()) + MM.append(d.pair(e)) L.append(MM) return (MatrixSpace(QQ, 4, 4)(L)).determinant().sqrt() @@ -1767,7 +1771,8 @@ def ternary_quadratic_form(self, include_basis=False): This function computes the positive definition quadratic form obtained by letting G be the trace zero subspace of `\ZZ` + - 2* ``self``, which has rank 3, and restricting the pairing:: + 2* ``self``, which has rank 3, and restricting the pairing + :meth:`QuaternionAlgebraElement_abstract.pair`:: (x,y) = (x.conjugate()*y).reduced_trace() @@ -2336,9 +2341,8 @@ def gram_matrix(self): [ 1920 16320 15360 19200] """ A = self.gens() - B = [z.conjugate() for z in A] two = QQ(2) - m = [two * (a * b).reduced_trace() for b in B for a in A] + m = [two * a.pair(b) for b in A for a in A] M44 = MatrixSpace(QQ, 4, 4) return M44(m, coerce=False) @@ -2811,7 +2815,7 @@ def intersection_of_row_modules_over_ZZ(v): return intersection_of_row_modules_over_ZZ([w] + v[2:]) -def normalize_basis_at_p(e, p, B = lambda x,y: (x*y.conjugate()).reduced_trace()): +def normalize_basis_at_p(e, p, B=QuaternionAlgebraElement_abstract.pair): r""" Compute a (at ``p``) normalized basis from the given basis ``e`` of a `\ZZ`-module. @@ -2835,8 +2839,8 @@ def normalize_basis_at_p(e, p, B = lambda x,y: (x*y.conjugate()).reduced_trace() - ``p`` -- prime for at which the basis should be normalized - - ``B`` -- (default: - ``lambda x,y: ((x*y).conjugate()).reduced_trace()``) + - ``B`` -- + (default: :meth:`QuaternionAlgebraElement_abstract.pair`) a bilinear form with respect to which to normalize OUTPUT: From a650a7da01550aa179490bf2fe5ebb1d806eba75 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 20 Oct 2021 11:20:10 +0100 Subject: [PATCH 093/122] remove more refs to MPIR in deps etc and few in docs too. --- build/pkgs/4ti2/spkg-configure.m4 | 2 +- build/pkgs/gfortran/SPKG.rst | 2 +- build/pkgs/mpc/spkg-install.in | 2 +- build/pkgs/mpir/distros/void.txt | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 build/pkgs/mpir/distros/void.txt diff --git a/build/pkgs/4ti2/spkg-configure.m4 b/build/pkgs/4ti2/spkg-configure.m4 index a16da8fc4b7..5b746aa1211 100644 --- a/build/pkgs/4ti2/spkg-configure.m4 +++ b/build/pkgs/4ti2/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([4ti2], [ - SAGE_SPKG_DEPCHECK([gmp mpir glpk zlib], [ + SAGE_SPKG_DEPCHECK([gmp glpk zlib], [ dnl Debian installs these programs with an executable prefix "4ti2-", dnl OpenSUSE uses the prefix "4ti2_". dnl Singular checks for unprefixed and prefixed with "4ti2-". diff --git a/build/pkgs/gfortran/SPKG.rst b/build/pkgs/gfortran/SPKG.rst index a40a1c4ce58..d66a7b0f173 100644 --- a/build/pkgs/gfortran/SPKG.rst +++ b/build/pkgs/gfortran/SPKG.rst @@ -22,7 +22,7 @@ Dependencies ------------ - zlib -- MPIR +- GMP - MPFR - MPC diff --git a/build/pkgs/mpc/spkg-install.in b/build/pkgs/mpc/spkg-install.in index 1e523e7f6f2..3ee37482fd4 100644 --- a/build/pkgs/mpc/spkg-install.in +++ b/build/pkgs/mpc/spkg-install.in @@ -1,7 +1,7 @@ cd src # Unset CC and CFLAGS. This will make mpc use the same configuration -# as MPIR, which is probably a good thing. +# as GMP, which is probably a good thing. unset CC unset CFLAGS diff --git a/build/pkgs/mpir/distros/void.txt b/build/pkgs/mpir/distros/void.txt deleted file mode 100644 index c490ad530ef..00000000000 --- a/build/pkgs/mpir/distros/void.txt +++ /dev/null @@ -1 +0,0 @@ -mpir-devel From 79651e9e07353bcf93c4915af604d17509dd4631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Oct 2021 13:08:24 +0200 Subject: [PATCH 094/122] get rid of [^']+)'>") - -def normalize_type_repr(s): - r""" - Convert the repr of type objects (e.g. ``int``, ``float``) from their - Python 2 representation to their Python 3 representation. - - In Python 2, the repr of built-in types like ``int`` is like - ````, whereas user-defined pure Python classes are displayed - as ````. On Python 3 this was normalized so that - built-in types are represented the same as user-defined classes (e.g. - ````. - - This simply normalizes all class/type reprs to the Python 3 convention for - the sake of output checking. - - EXAMPLES:: - - sage: from sage.doctest.parsing import normalize_type_repr - sage: s = "" - sage: normalize_type_repr(s) - "" - sage: normalize_type_repr(repr(float)) - "" - - This can work on multi-line output as well:: - - sage: s = "The desired output was \n" - sage: s += "The received output was " - sage: print(normalize_type_repr(s)) - The desired output was - The received output was - - And should work when types are embedded in other nested expressions:: - - sage: normalize_type_repr(repr([Integer, float])) - "[, ]" - """ - - def subst(m): - return "".format(m.group('name')) - - return _type_repr_re.sub(subst, s) - - _long_repr_re = re.compile(r'([+-]?[0-9]+)[lL]') def normalize_long_repr(s): """ @@ -284,10 +239,6 @@ def subst(m): _repr_fixups = [ (lambda g, w: 'u"' in w or "u'" in w, lambda g, w: (g, remove_unicode_u(w))), - - (lambda g, w: ' + sage: marked = parse_tolerance("sage: s.update(tol = 0.1); s.rel_tol # abs tol 0.01 ", "") sage: marked.tol 0 @@ -1088,14 +1039,6 @@ def check_output(self, want, got, optionflags): [u'Fermat', u'Euler'] sage: c = u'you'; c u'you' - - Also allowance for the difference in reprs of ``type`` instances (i.e. - classes) between Python 2 and Python 3:: - - sage: int - - sage: float - """ got = self.human_readable_escape_sequences(got) got = glpk_simplex_warning_regex.sub('', got) diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index 96263cd0be9..2eebaa7b413 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -384,7 +384,7 @@ cdef class ECModularSymbol: sage: E = EllipticCurve('11a') sage: M = ECModularSymbol(E) sage: M.__reduce__() - (, + (, (Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field, 1)) """ diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx index 09f781d4a2c..b37ae49a37f 100644 --- a/src/sage/matroids/unpickling.pyx +++ b/src/sage/matroids/unpickling.pyx @@ -316,7 +316,7 @@ def unpickle_plus_minus_one_matrix(version, data): sage: M PlusMinusOneMatrix instance with 2 rows and 2 columns sage: type(M) - + sage: M.__reduce__()[1][1] (2, 2, [1, 0, -1, 1]) """ diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py index ddb05e44819..7b579f45a4d 100644 --- a/src/sage/misc/call.py +++ b/src/sage/misc/call.py @@ -120,7 +120,7 @@ def __hash__(self): sage: hash(x) # random # indirect doctest 210434060 sage: type(hash(x)) - + sage: y = attrcall('core', 3, blah = 1, flatten = True) sage: hash(y) == hash(x) True diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 4eeccd1de7f..62d49bab39a 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -219,7 +219,7 @@ def find_objects_from_name(name, module_name=None): sage: import sage.misc.dev_tools as dt sage: dt.find_objects_from_name('FareySymbol') - [] + [] sage: import sympy sage: dt.find_objects_from_name('RR') diff --git a/src/sage/misc/explain_pickle.py b/src/sage/misc/explain_pickle.py index 448e641191c..bc1cefbd759 100644 --- a/src/sage/misc/explain_pickle.py +++ b/src/sage/misc/explain_pickle.py @@ -408,7 +408,7 @@ def __init__(self, klass): sage: from sage.misc.explain_pickle import * sage: PickleInstance(Integer).klass - + """ self.klass = klass diff --git a/src/sage/misc/fast_methods.pyx b/src/sage/misc/fast_methods.pyx index 1165b7caccc..eb73cb6bc3e 100644 --- a/src/sage/misc/fast_methods.pyx +++ b/src/sage/misc/fast_methods.pyx @@ -105,7 +105,7 @@ cdef class WithEqualityById: """ def __hash__(self): """ - The hash provided by this class coincides with that of ````. + The hash provided by this class coincides with that of ````. TESTS:: diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 45d74ea749a..8eba015bc73 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -178,9 +178,9 @@ def coerce(P, x): EXAMPLES:: sage: type(5) - + sage: type(coerce(QQ,5)) - + """ try: return P._coerce_(x) @@ -1390,7 +1390,7 @@ def numerical_approx(x, prec=None, digits=None, algorithm=None): 1.41421356237309*I sage: type(numerical_approx(CC(1/2))) - + The following tests :trac:`10761`, in which ``n()`` would break when called on complex-valued algebraic numbers. :: @@ -1536,11 +1536,11 @@ def round(x, ndigits=0): sage: q = round(sqrt(2),5); q 1.41421 sage: type(q) - + sage: q = round(sqrt(2)); q 1 sage: type(q) - + sage: round(pi) 3 sage: b = 5.4999999999999999 @@ -1550,7 +1550,7 @@ def round(x, ndigits=0): This example addresses :trac:`23502`:: sage: n = round(6); type(n) - + Since we use floating-point with a limited range, some roundings can't be performed:: diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 56a47ea4513..b7dbccdbcd8 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -553,7 +553,7 @@ def has_latex_attr(x) -> bool: but calling it is broken:: sage: T = type(identity_matrix(3)); T - + sage: hasattr(T, '_latex_') True sage: T._latex_() diff --git a/src/sage/misc/lazy_attribute.pyx b/src/sage/misc/lazy_attribute.pyx index 9cee5d3c4b4..a2edb96af1a 100644 --- a/src/sage/misc/lazy_attribute.pyx +++ b/src/sage/misc/lazy_attribute.pyx @@ -454,7 +454,7 @@ class lazy_attribute(_lazy_attribute): sage: cython('\n'.join(cython_code)) sage: P = MyParent(category=Rings()) sage: P.element_class # indirect doctest - + .. rubric:: About descriptor specifications diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index 4d4ee94bbe9..fcc4291ebc3 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -11,7 +11,7 @@ EXAMPLES:: sage: lazy_import('sage.rings.all', 'ZZ') sage: type(ZZ) - + sage: ZZ(4.0) 4 @@ -468,7 +468,7 @@ cdef class LazyImport(object): sage: lazy_import('sys', 'version_info') sage: type(version_info) - + sage: len(version_info) 5 """ @@ -519,7 +519,7 @@ cdef class LazyImport(object): sage: class Bar(Foo): ....: pass sage: type(Foo.__dict__['plot']) - + We access the ``plot`` method:: @@ -978,7 +978,7 @@ def lazy_import(module, names, as_=None, *, sage: lazy_import('sage.rings.all', 'ZZ') sage: type(ZZ) - + sage: ZZ(4.0) 4 sage: lazy_import('sage.rings.all', 'RDF', 'my_RDF') @@ -1011,7 +1011,7 @@ def lazy_import(module, names, as_=None, *, sage: class Bar(Foo): ....: pass sage: type(Foo.__dict__['plot']) - + sage: 'EXAMPLES' in Bar.plot.__doc__ True sage: type(Foo.__dict__['plot']) diff --git a/src/sage/misc/persist.pyx b/src/sage/misc/persist.pyx index 84db8111e27..e19dba5db83 100644 --- a/src/sage/misc/persist.pyx +++ b/src/sage/misc/persist.pyx @@ -531,19 +531,19 @@ def unpickle_global(module, name): sage: from sage.misc.persist import unpickle_override, register_unpickle_override sage: unpickle_global('sage.rings.integer', 'Integer') - + Now we horribly break the pickling system:: sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational')) sage: unpickle_global('sage.rings.integer', 'Integer') - + and we reach into the internals and put it back:: sage: del unpickle_override[('sage.rings.integer', 'Integer')] sage: unpickle_global('sage.rings.integer', 'Integer') - + A meaningful error message with resolution instructions is displayed for old pickles that accidentally got broken because a class or entire module diff --git a/src/sage/misc/sage_eval.py b/src/sage/misc/sage_eval.py index 029ed54df20..f65e90fce1a 100644 --- a/src/sage/misc/sage_eval.py +++ b/src/sage/misc/sage_eval.py @@ -216,14 +216,14 @@ def sageobj(x, vars=None): EXAMPLES:: sage: type(sageobj(gp('34/56'))) - + sage: n = 5/2 sage: sageobj(n) is n True sage: k = sageobj('Z(8^3/1)', {'Z':ZZ}); k 512 sage: type(k) - + This illustrates interfaces:: @@ -233,12 +233,12 @@ def sageobj(x, vars=None): sage: f._sage_() 2/3 sage: type(f._sage_()) - + sage: a = gap(939393/2433) sage: a._sage_() 313131/811 sage: type(a._sage_()) - + """ try: return x._sage_() diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 2ff2762f156..537c763206d 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -529,7 +529,7 @@ def visit_NameConstant(self, node): sage: [vis(n) for n in ['True', 'False', 'None']] # py3 [True, False, None] sage: [type(vis(n)) for n in ['True', 'False', 'None']] # py3 - [, , ] + [, , ] """ return node.value diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index 384dd3400b5..83689f81bc5 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -571,7 +571,7 @@ cdef class Farey: Serialization for pickling:: sage: FareySymbol(Gamma0(4)).__reduce__() - (, ...)) + (, ...)) """ return Farey, (self.group, self.this_ptr.dumps()) diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index 65942454937..bf11fd0c6b0 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -815,7 +815,7 @@ cdef class Dist_vector(Dist): sage: D = sage.modular.pollack_stevens.distributions.Symk(2) sage: x = D([2,3,4]) sage: x.__reduce__() - (, ((2, 3, 4), Sym^2 Q^2, 0, False)) + (, ((2, 3, 4), Sym^2 Q^2, 0, False)) """ return (self.__class__, (self._moments, self.parent(), self.ordp, False)) diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index 5a0c9265d34..28e359dbf00 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -730,7 +730,7 @@ def split(self): ....: E((-2)^x) ....: except PartialConversionValueError as e: ....: e.element.split() - (2^x, element with parameter -1 () in Growth Group ZZ^x) + (2^x, element with parameter -1 () in Growth Group ZZ^x) TESTS:: diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 5fc4a0e6d4d..32f418a12f6 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -82,7 +82,7 @@ cdef class FiniteField(Field): def __hash__(self): """ - The hash provided by this class coincides with that of ````. + The hash provided by this class coincides with that of ````. TESTS:: diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index bba2fc46afc..e88865f2ec9 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -425,7 +425,7 @@ cdef class NumberFieldElement(FieldElement): sage: k. = NumberField(x^3 - 17*x^2 + 1) sage: t = a.__reduce__(); t - (, + (, (Number Field in a with defining polynomial x^3 - 17*x^2 + 1, x)) sage: t[0](*t[1]) == a True diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index bb28bb1ee59..cf63cba4bce 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -228,7 +228,7 @@ def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): ....: if tiny(x.imag()): return x.real() ....: if tiny(x.real()): return CIF(0, x.imag()) sage: rts = complex_roots(p); type(rts[0][0]), sorted(map(smash, rts)) - (, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) + (, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic'); type(rts[0][0]), sorted(map(smash, rts)) (, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic_real'); type(rts[0][0]), rts diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 7c71af6fb75..25d70431aa9 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -308,7 +308,7 @@ cdef class Polynomial_rational_flint(Polynomial): sage: R. = QQ[] sage: f = 2/3 * t^2 + 1 sage: r = f.__reduce__(); r - (, (Univariate Polynomial Ring in t over Rational Field, [1, 0, 2/3], False, False)) + (, (Univariate Polynomial Ring in t over Rational Field, [1, 0, 2/3], False, False)) sage: r[0](*r[1]) 2/3*t^2 + 1 sage: loads(dumps(f)) == f diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 640971fb47e..e6708a2f81f 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -378,7 +378,7 @@ class RingExtensionFactory(UniqueFactory): From: Integer Ring To: Rational Field Defn: 1 |--> 1, (), ()), - {'constructors': [(, + {'constructors': [(, {'is_backend_exposed': True, 'print_options': {'print_elements_as': None, 'print_parent_as': None}})]}) @@ -387,7 +387,7 @@ class RingExtensionFactory(UniqueFactory): From: Finite Field in z2 of size 5^2 To: Finite Field in z4 of size 5^4 Defn: z2 |--> z4^3 + z4^2 + z4 + 3, (z4,), ('a',)), - {'constructors': [(, + {'constructors': [(, {'gen': z4, 'is_backend_exposed': True, 'names': ('a',)})]}) """ use_generic_constructor = True diff --git a/src/sage/rings/semirings/tropical_semiring.pyx b/src/sage/rings/semirings/tropical_semiring.pyx index 8466b9c47b0..5ae1ea93bf7 100644 --- a/src/sage/rings/semirings/tropical_semiring.pyx +++ b/src/sage/rings/semirings/tropical_semiring.pyx @@ -72,7 +72,7 @@ cdef class TropicalSemiringElement(Element): sage: T = TropicalSemiring(QQ) sage: elt = T(2) sage: elt.__reduce__() - (, + (, (Tropical semiring over Rational Field, 2)) """ return (TropicalSemiringElement, (self.parent(), self._val)) diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index c3761beddbf..caa0cc58258 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -935,7 +935,7 @@ cdef class ClonableArray(ClonableElement): [1, 2, 4] sage: t = el.__reduce__(); t (, - (, + (, , [1, 2, 4], True, @@ -1721,7 +1721,7 @@ cdef class ClonableIntArray(ClonableElement): [1, 2, 4] sage: t = el.__reduce__(); t (, - (, + (, , [1, 2, 4], True, diff --git a/src/sage/symbolic/pynac_constant_impl.pxi b/src/sage/symbolic/pynac_constant_impl.pxi index 20a407c2241..9dd5366dcaa 100644 --- a/src/sage/symbolic/pynac_constant_impl.pxi +++ b/src/sage/symbolic/pynac_constant_impl.pxi @@ -120,7 +120,7 @@ cdef class PynacConstant: sage: f + 2 Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for +: '' and 'Integer Ring' + TypeError: unsupported operand parent(s) for +: '' and 'Integer Ring' sage: foo = f.expression(); foo foo From 2f0c8282bee61453e962f57cdc2a16be746ba097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Oct 2021 13:39:32 +0200 Subject: [PATCH 095/122] describe known bugs in derivative of piecewise --- src/sage/functions/piecewise.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 4ac28309a5d..540162de07f 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -296,6 +296,15 @@ def _tderivative_(self, parameters, variable, *args, **kwds): sage: f.diff(x,x) piecewise(x|-->2 on (-1, 1), x|-->6*x on (1, 3); x) + This still fails miserably:: + + sage: y = SR.var('y') + sage: f = piecewise([ [(-6,0), x+y], [(0,8), x*y]],var=x) + sage: f.derivative(x) # known bug + piecewise(x|-->1 on (-6, 0), x|-->y on (0, 8); x) + sage: f.derivative(y) # known bug + piecewise(x|-->1 on (-6, 0), x|-->x on (0, 8); x) + TESTS:: sage: f = piecewise([((-oo, -1),0), ((-1, 1),exp(-1/(1 - x^2))), ((1, oo),0)]) From f004d4d69923cb7277799addbb0aa6cdb3ef8960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Oct 2021 16:30:10 +0200 Subject: [PATCH 096/122] work done on annotations in combinat --- .../quiver_mutation_type.py | 10 ++-- src/sage/combinat/combinat.py | 17 ++++--- src/sage/combinat/composition.py | 6 +-- src/sage/combinat/dyck_word.py | 51 +++++++++---------- src/sage/combinat/gelfand_tsetlin_patterns.py | 5 +- src/sage/combinat/growth.py | 22 ++++---- src/sage/combinat/interval_posets.py | 24 ++++----- src/sage/combinat/partition_tuple.py | 2 +- src/sage/combinat/permutation.py | 10 ++-- src/sage/combinat/plane_partition.py | 4 +- src/sage/combinat/posets/hasse_diagram.py | 10 ++-- src/sage/combinat/superpartition.py | 18 ++++--- src/sage/combinat/tamari_lattices.py | 8 +-- 13 files changed, 95 insertions(+), 92 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py index f697451a99c..6da5e19b8d8 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py @@ -2089,7 +2089,7 @@ def class_size(self): multiplicities[i]) for i in range(len(sizes))) - def dual(self) -> QuiverMutationType: + def dual(self): """ Return the QuiverMutationType which is dual to ``self``. @@ -2104,7 +2104,7 @@ def dual(self) -> QuiverMutationType: return QuiverMutationType([comp.dual() for comp in comps]) -def _construct_classical_mutation_classes(n) -> dict: +def _construct_classical_mutation_classes(n) -> dict[tuple, list | set]: r""" Return a dict with keys being tuples representing regular QuiverMutationTypes of the given rank, and with values being lists @@ -2123,7 +2123,7 @@ def _construct_classical_mutation_classes(n) -> dict: ('AO', (((0, 1), (4, -1)),))] """ from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver - data = {} + data: dict[tuple, set | list] = {} # finite A data[('A', n)] = ClusterQuiver(['A', n]).mutation_class(data_type='dig6') @@ -2161,7 +2161,7 @@ def _construct_classical_mutation_classes(n) -> dict: return data -def _construct_exceptional_mutation_classes(n) -> dict: +def _construct_exceptional_mutation_classes(n) -> dict[tuple, list | set]: r""" Return a dict with keys being tuples representing exceptional QuiverMutationTypes of the given rank, and with values being lists @@ -2185,7 +2185,7 @@ def _construct_exceptional_mutation_classes(n) -> dict: ('BP_', (((0, 1), (2, -2)), ((1, 2), (1, -3)), ((2, 0), (3, -1))))] """ from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver - data = {} + data: dict[tuple, list | set] = {} # finite E if n in [6, 7, 8]: data[('E', n)] = ClusterQuiver(['E', n]).mutation_class(data_type='dig6') diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index d366cc15930..67a2a82bd88 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -148,6 +148,7 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations from typing import Iterator from sage.rings.all import ZZ, QQ, Integer, infinity @@ -552,7 +553,7 @@ def euler_number(n, algorithm='flint') -> Integer: if n < 0: raise ValueError("n (=%s) must be a nonnegative integer" % n) if algorithm == 'maxima': - return ZZ(maxima.euler(n)) + return ZZ(maxima.euler(n)) # type:ignore elif algorithm == 'flint': import sage.libs.flint.arith return sage.libs.flint.arith.euler_number(n) @@ -990,7 +991,7 @@ def stirling_number2(n, k, algorithm=None) -> Integer: from sage.libs.gap.libgap import libgap return libgap.Stirling2(n, k).sage() elif algorithm == 'maxima': - return ZZ(maxima.stirling2(n, k)) + return ZZ(maxima.stirling2(n, k)) # type:ignore else: raise ValueError("unknown algorithm: %s" % algorithm) @@ -1691,7 +1692,7 @@ def __hash__(self): """ return hash(repr(self)) - def __cardinality_from_iterator(self) -> Integer: + def __cardinality_from_iterator(self) -> Integer | infinity: """ Default implementation of cardinality which just goes through the iterator of the combinatorial class to count the number of objects. @@ -1709,6 +1710,7 @@ def __cardinality_from_iterator(self) -> Integer: for _ in self: c += one return c + cardinality = __cardinality_from_iterator # __call__, element_class, and _element_constructor_ are poor @@ -2256,7 +2258,7 @@ def __contains__(self, x) -> bool: """ return x in self.left_cc or x in self.right_cc - def cardinality(self) -> Integer: + def cardinality(self) -> Integer | infinity: """ EXAMPLES:: @@ -2443,7 +2445,7 @@ def __repr__(self) -> str: else: return "Image of %s by %s" % (self.cc, self.f) - def cardinality(self) -> Integer: + def cardinality(self) -> Integer | infinity: """ Return the cardinality of this combinatorial class @@ -2452,7 +2454,6 @@ def cardinality(self) -> Integer: sage: R = Permutations(10).map(attrcall('reduced_word')) sage: R.cardinality() 3628800 - """ return self.cc.cardinality() @@ -2494,7 +2495,7 @@ class InfiniteAbstractCombinatorialClass(CombinatorialClass): self._infinite_cclass_slice is supposed to accept any integer as an argument and return something which is iterable. """ - def cardinality(self): + def cardinality(self) -> Integer | infinity: """ Count the elements of the combinatorial class. @@ -2945,7 +2946,7 @@ def bell_polynomial(n: Integer, k: Integer): R = PolynomialRing(ZZ, 'x', n - k + 1) vars = R.gens() result = R.zero() - for p in Partitions(n, length=k): + for p in Partitions(n, length=k): # type:ignore factorial_product = 1 power_factorial_product = 1 for part, count in p.to_exp_dict().items(): diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 930ae037346..5f899c71106 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -617,7 +617,7 @@ def join(self, other, check=True) -> Composition: if check and (sum(self) != sum(other)): raise ValueError("{} is not the same size as {}".format(self, other)) - factors = [] + factors: list[int] = [] I_iter = iter(self) i = 0 @@ -893,7 +893,7 @@ def fatter(self): """ return Compositions(len(self)).map(self.fatten) - def refinement_splitting(self, J) -> Composition: + def refinement_splitting(self, J) -> list[Composition]: r""" Return the refinement splitting of ``self`` according to ``J``. @@ -1797,7 +1797,7 @@ def from_code(self, code) -> Composition: [3, 1, 2, 3, 5] """ if code == [0]: - return [] + return self.element_class(self, []) L = [x for x in range(len(code)) if code[x] == 1] # the positions of the letter 1 c = [L[i] - L[i - 1] for i in range(1, len(L))] + [len(code) - L[-1]] diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 4eb3cebf1b4..a3c80c12115 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -78,7 +78,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Union from .combinat import CombinatorialElement, catalan_number from sage.combinat.combinatorial_map import combinatorial_map @@ -1071,7 +1070,7 @@ def heights(self) -> tuple: heights[i + 1] = height return tuple(heights) - def associated_parenthesis(self, pos) -> Union[int, None]: + def associated_parenthesis(self, pos) -> int | None: r""" Report the position for the parenthesis in ``self`` that matches the one at position ``pos``. @@ -1177,12 +1176,12 @@ def ascent_prime_decomposition(self) -> list: break j += 1 else: - result.extend([DyckWord([open_symbol] * up), - DyckWord(self[i:j])]) + result.extend([DyckWord([open_symbol] * up), # type: ignore + DyckWord(self[i:j])]) # type: ignore i = j up = 0 - result.append(DyckWord([open_symbol] * up)) + result.append(DyckWord([open_symbol] * up)) # type:ignore return result def catalan_factorization(self) -> list: @@ -1219,7 +1218,7 @@ def catalan_factorization(self) -> list: result = [] while i <= n: if H[j] == h or j == i: - result.append(DyckWord(self[i:j])) + result.append(DyckWord(self[i:j])) # type:ignore h += 1 i = j + 1 j = n @@ -1959,7 +1958,7 @@ def reading_permutation(self) -> Permutation: """ alist = self.to_area_sequence() if not alist: - return Permutation([]) + return Permutation([]) # type: ignore m = max(alist) p1 = Word([m - alist[-i - 1] for i in range(len(alist))]).standard_permutation() @@ -2028,13 +2027,13 @@ def to_pair_of_standard_tableaux(self) -> tuple: from sage.combinat.tableau import Tableau n = self.semilength() if n == 0: - return (Tableau([]), Tableau([])) + return (Tableau([]), Tableau([])) # type: ignore elif self.height() == n: - T = Tableau([list(range(1, n + 1))]) + T = Tableau([list(range(1, n + 1))]) # type: ignore return (T, T) else: - left = [[], []] - right = [[], []] + left: list[list[int]] = [[], []] + right:list[list[int]] = [[], []] for pos in range(n): if self[pos] == open_symbol: left[0].append(pos + 1) @@ -2044,7 +2043,7 @@ def to_pair_of_standard_tableaux(self) -> tuple: right[0].append(pos + 1) else: right[1].append(pos + 1) - return (Tableau(left), Tableau(right)) + return (Tableau(left), Tableau(right)) # type: ignore @combinatorial_map(name='to 312 avoiding permutation') def to_312_avoiding_permutation(self) -> Permutation: @@ -2117,7 +2116,7 @@ def to_noncrossing_permutation(self) -> Permutation: """ n = self.semilength() if n == 0: - return Permutation([]) + return Permutation([]) # type: ignore D, touch_sequence = pealing(self, return_touches=True) pi = list(range(1, n + 1)) while touch_sequence: @@ -2218,7 +2217,7 @@ def to_132_avoiding_permutation(self) -> Permutation: v = min(v for v in values if v > n - i - area[n - i - 1]) pi.append(v) values.remove(v) - return Permutation(pi) + return Permutation(pi) # type: ignore def to_permutation(self, map) -> Permutation: r""" @@ -2394,7 +2393,7 @@ def to_Catalan_code(self) -> list: cut = self.associated_parenthesis(0) if cut is None: raise ValueError('not valid for incomplete Dyck words') - recdw = DyckWord(self[1:cut] + self[cut + 1:]) + recdw = DyckWord(self[1:cut] + self[cut + 1:]) # type:ignore returns = [0] + recdw.returns_to_zero() res = recdw.to_Catalan_code() res.append(returns.index(cut - 1)) @@ -2715,7 +2714,7 @@ def reverse(self) -> DyckWord: else: list.append(open_symbol) list.reverse() - return DyckWord(list) + return DyckWord(list) # type:ignore def first_return_decomposition(self) -> tuple: r""" @@ -2733,7 +2732,7 @@ def first_return_decomposition(self) -> tuple: ([], [1, 0]) """ k = self.position_of_first_return() * 2 - return DyckWord(self[1:k - 1]), DyckWord(self[k:]) + return DyckWord(self[1:k - 1]), DyckWord(self[k:]) # type:ignore def decomposition_reverse(self) -> DyckWord: r""" @@ -2756,10 +2755,10 @@ def decomposition_reverse(self) -> DyckWord: """ if not self: return self - else: - D1, D2 = self.first_return_decomposition() - return DyckWord([1] + list(D2.decomposition_reverse()) - + [0] + list(D1.decomposition_reverse())) + D1, D2 = self.first_return_decomposition() + D = [1] + list(D2.decomposition_reverse()) + D += [0] + list(D1.decomposition_reverse()) + return DyckWord(D) # type:ignore @combinatorial_map(name="Area-dinv to bounce-area") def area_dinv_to_bounce_area_map(self) -> DyckWord: @@ -2805,7 +2804,7 @@ def area_dinv_to_bounce_area_map(self) -> DyckWord: image.append(1) elif j == i + 1: image.append(0) - return DyckWord(image) + return DyckWord(image) # type:ignore @combinatorial_map(name="Bounce-area to area-dinv") def bounce_area_to_area_dinv_map(self) -> DyckWord: @@ -2846,13 +2845,13 @@ def bounce_area_to_area_dinv_map(self) -> DyckWord: [1, 1, 0, 0] """ aseq = self.to_area_sequence() - out = [] - zeros = [] + out: list[int] = [] + zeros: list[int] = [] for i in range(len(aseq)): p = (zeros + [len(out)])[aseq[i]] out = [1] + out[p:] + [0] + out[:p] zeros = [0] + [j + len(out) - p for j in zeros[:aseq[i]]] - return DyckWord(out) + return DyckWord(out) # type:ignore def area(self) -> int: r""" @@ -2964,7 +2963,7 @@ def bounce_path(self) -> DyckWord: i -= 1 area_seq[i] = area_seq[i + 1] - 1 i -= 1 - return DyckWord(area_sequence=area_seq) + return DyckWord(area_sequence=area_seq) # type:ignore def bounce(self) -> int: r""" diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index 1ab6ef536fa..ff5a145cd92 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -1415,6 +1415,5 @@ def random_element(self) -> GelfandTsetlinPattern: """ if self._strict: return self._cftp(1) - else: - l = [i for i in self._row if i > 0] - return SemistandardTableaux(l, max_entry=self._n).random_element().to_Gelfand_Tsetlin_pattern() + l = [i for i in self._row if i > 0] + return SemistandardTableaux(l, max_entry=self._n).random_element().to_Gelfand_Tsetlin_pattern() # type:ignore diff --git a/src/sage/combinat/growth.py b/src/sage/combinat/growth.py index c92608b002c..47abe8d9c93 100644 --- a/src/sage/combinat/growth.py +++ b/src/sage/combinat/growth.py @@ -470,7 +470,7 @@ # # https://www.gnu.org/licenses/ # *************************************************************************** - +from __future__ import annotations from copy import copy from itertools import zip_longest @@ -489,6 +489,7 @@ from sage.combinat.shifted_primed_tableau import ShiftedPrimedTableau from sage.graphs.digraph import DiGraph + def _make_partition(l): """ Return the list as a partition. @@ -506,6 +507,7 @@ def _make_partition(l): """ return _Partitions.element_class(_Partitions, l) + class GrowthDiagram(SageObject): r""" A generalized Schensted growth diagram in the sense of Fomin. @@ -1226,7 +1228,7 @@ def _shape_from_labels(self, labels): is_P_edge = getattr(rule, "is_P_edge", None) is_Q_edge = getattr(rule, "is_Q_edge", None) if rule.has_multiple_edges: - def right_left(la, mu, e): + def right_left_multi(la, mu, e) -> int: if rule.rank(la) < rule.rank(mu): if is_Q_edge is not None and e not in is_Q_edge(la, mu): raise ValueError("%s has smaller rank than %s but there is no edge of color %s in Q" % (la, mu, e)) @@ -1235,13 +1237,12 @@ def right_left(la, mu, e): if is_P_edge is not None and e not in is_P_edge(mu, la): raise ValueError("%s has smaller rank than %s but there is no edge of color %s in P" % (mu, la, e)) return 0 - else: - raise ValueError("can only determine the shape of the growth" - " diagram if ranks of successive labels differ") - return _Partitions.from_zero_one([right_left(labels[i], labels[i+2], labels[i+1]) + raise ValueError("can only determine the shape of the growth" + " diagram if ranks of successive labels differ") + return _Partitions.from_zero_one([right_left_multi(labels[i], labels[i+2], labels[i+1]) for i in range(0, len(labels)-2, 2)]) else: - def right_left(la, mu): + def right_left(la, mu) -> int: if rule.rank(la) < rule.rank(mu): if is_Q_edge is not None and not is_Q_edge(la, mu): raise ValueError("%s has smaller rank than %s but is not covered by it in Q" % (la, mu)) @@ -1250,9 +1251,8 @@ def right_left(la, mu): if is_P_edge is not None and not is_P_edge(mu, la): raise ValueError("%s has smaller rank than %s but is not covered by it in P" % (mu, la)) return 0 - else: - raise ValueError("can only determine the shape of the growth" - " diagram if ranks of successive labels differ") + raise ValueError("can only determine the shape of the growth" + " diagram if ranks of successive labels differ") return _Partitions.from_zero_one([right_left(labels[i], labels[i+1]) for i in range(len(labels)-1)]) @@ -2963,7 +2963,7 @@ class RuleSylvester(Rule): sage: list(Sylvester(labels=G.out_labels())) == list(G) True """ - zero = BinaryTree() + zero = BinaryTree() # type: ignore def normalize_vertex(self, v): r""" diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index ed2de3bd9ce..48fbe140f11 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -537,7 +537,7 @@ def draw_decreasing(i, j) -> str: return start + nodes + relations + end - def poset(self) -> Poset: + def poset(self): r""" Return ``self`` as a labelled poset. @@ -594,7 +594,7 @@ def _mul_(self, other: TIP) -> TIP: relations.extend([(i + n, j + n) for i, j in other._poset.cover_relations_iterator()]) P = FinitePoset(DiGraph([list(range(1, n + m + 1)), relations], - format='vertices_and_edges')) + format='vertices_and_edges')) # type:ignore return TamariIntervalPoset(P, check=False) # type:ignore def __hash__(self): @@ -1057,7 +1057,7 @@ def complement(self) -> TIP: new_covers = [[N - i, N - j] for i, j in self._poset.cover_relations_iterator()] P = FinitePoset(DiGraph([list(range(1, N)), new_covers], - format='vertices_and_edges')) + format='vertices_and_edges')) # type:ignore return TamariIntervalPoset(P, check=False) # type:ignore def left_branch_involution(self) -> TIP: @@ -1709,7 +1709,7 @@ def initial_forest(self) -> TIP: """ relations = self.increasing_cover_relations() P = FinitePoset(DiGraph([list(range(1, self._size + 1)), relations], - format='vertices_and_edges')) + format='vertices_and_edges')) # type:ignore return TamariIntervalPoset(P, check=False) # type:ignore def final_forest(self) -> TIP: @@ -1729,7 +1729,7 @@ def final_forest(self) -> TIP: """ relations = self.decreasing_cover_relations() P = FinitePoset(DiGraph([list(range(1, self._size + 1)), relations], - format='vertices_and_edges')) + format='vertices_and_edges')) # type:ignore return TamariIntervalPoset(P, check=False) # type:ignore def is_initial_interval(self) -> bool: @@ -1979,7 +1979,7 @@ def add(perm: list, i): perm: list[int] = [] for i in sorted(final_forest.sinks()): add(perm, i) - return Permutation(perm) + return Permutation(perm) # type:ignore def max_linear_extension(self) -> Permutation: r""" @@ -2029,7 +2029,7 @@ def add(perm: list, i): perm: list[int] = [] for i in sorted(initial_forest.sinks(), reverse=True): add(perm, i) - return Permutation(perm) + return Permutation(perm) # type:ignore def linear_extensions(self) -> Iterator: r""" @@ -2051,7 +2051,7 @@ def linear_extensions(self) -> Iterator: [[4, 1, 2, 3], [1, 2, 4, 3], [1, 4, 2, 3]] """ for ext in self._poset.linear_extensions(): - yield Permutation(ext) + yield Permutation(ext) # type:ignore def lower_contained_intervals(self) -> Iterator: r""" @@ -3045,7 +3045,7 @@ def get_relations(bt, start=1): roots, relations, index = get_relations(binary_tree) P = FinitePoset(DiGraph([list(range(1, index)), relations], - format='vertices_and_edges')) + format='vertices_and_edges')) # type:ignore return TamariIntervalPoset(P, check=False) # type:ignore @staticmethod @@ -3153,7 +3153,7 @@ def get_relations(bt, start=1): roots, relations, index = get_relations(binary_tree) P = FinitePoset(DiGraph([list(range(1, index)), relations], - format='vertices_and_edges')) + format='vertices_and_edges')) # type:ignore return TamariIntervalPoset(P, check=False) # type:ignore @staticmethod @@ -3470,8 +3470,8 @@ def profil(gr, vertex): if u[2] == color_b]) dyckword_top += [1] + [0] * indegree1 - dyckword_bottom = DyckWord(dyckword_bottom) - dyckword_top = DyckWord(dyckword_top) + dyckword_bottom = DyckWord(dyckword_bottom) # type:ignore + dyckword_top = DyckWord(dyckword_top) # type:ignore TIP = TamariIntervalPosets(len(dyckword_bottom) // 2) return TIP.from_dyck_words(dyckword_bottom, dyckword_top) diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 34b5a9e46ff..825344b9e03 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -613,7 +613,7 @@ def _repr_compact_high(self): return '%s' % '|'.join(mu._repr_compact_high() for mu in self) # override default string representation which is str(self._list) - __str__ = lambda self: self._repr_() + __str__ = lambda self: self._repr_() # type: ignore def _latex_(self): r""" diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 72947e020a1..4b39eda2e2f 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1629,8 +1629,8 @@ def stack_sort(self) -> Permutation: sage: p.stack_sort() [1] """ - stack = [] - sorted_p = [] + stack: list[int] = [] + sorted_p: list[int] = [] for j in self: if stack: for i in reversed(stack): @@ -1641,7 +1641,7 @@ def stack_sort(self) -> Permutation: break stack.append(j) sorted_p.extend(reversed(stack)) - return Permutation(sorted_p) + return Permutation(sorted_p) # type: ignore def to_digraph(self) -> DiGraph: r""" @@ -2192,7 +2192,7 @@ def longest_increasing_subsequence_length(self) -> Integer: sage: Permutation([]).longest_increasing_subsequence_length() 0 """ - r = [] + r: list[int] = [] for x in self: if max(r+[0]) > x: y = min(z for z in r if z > x) @@ -2380,7 +2380,7 @@ def foata_bijection(self) -> Permutation: sage: Permutation([1]).foata_bijection() [1] """ - M = [] + M: list[int] = [] for e in self: k = len(M) if k <= 1: diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index 857924a64a5..e65625f6465 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -847,8 +847,8 @@ def __iter__(self) -> Iterator[PP]: A = self._box[0] B = self._box[1] C = self._box[2] - from sage.combinat.tableau import SemistandardTableaux - for T in SemistandardTableaux([B for i in range(A)], max_entry=C + A): + from sage.combinat.tableau import SemistandardTableaux as SST + for T in SST([B for i in range(A)], max_entry=C + A): # type:ignore PP = [[0 for _ in range(B)] for _ in range(A)] for r in range(A): for c in range(B): diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 9d0a62a1f5a..93d254b245c 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -15,6 +15,8 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations + from sage.graphs.digraph import DiGraph from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ @@ -1852,7 +1854,7 @@ def vertical_decomposition(self, return_list=False): result.pop() # Remove the top element. return result - def is_complemented(self) -> bool: + def is_complemented(self) -> int | None: """ Return an element of the lattice that has no complement. @@ -3420,18 +3422,18 @@ def is_congruence_normal(self) -> bool: from sage.combinat.set_partition import SetPartition n = self.order() - congs_ji = {} + congs_ji: dict[SetPartition, list] = {} for ji in range(n): if self.in_degree(ji) == 1: - cong = SetPartition(self.congruence([[ji, next(self.neighbor_in_iterator(ji))]])) + cong = SetPartition(self.congruence([[ji, next(self.neighbor_in_iterator(ji))]])) # type: ignore if cong not in congs_ji: congs_ji[cong] = [] congs_ji[cong].append(ji) for mi in range(n): if self.out_degree(mi) == 1: - cong = SetPartition(self.congruence([[mi, next(self.neighbor_out_iterator(mi))]])) + cong = SetPartition(self.congruence([[mi, next(self.neighbor_out_iterator(mi))]])) # type: ignore if any(self.is_lequal(ji, mi) for ji in congs_ji[cong]): return False diff --git a/src/sage/combinat/superpartition.py b/src/sage/combinat/superpartition.py index 3de4852411d..18be88eb359 100644 --- a/src/sage/combinat/superpartition.py +++ b/src/sage/combinat/superpartition.py @@ -357,7 +357,7 @@ def to_partition(self) -> Partition: sage: SuperPartition([[2,1,0],[3,3]]).to_partition().parent() Partitions """ - return Partition(sorted(self[0] + self[1], reverse=True)) + return Partition(sorted(self[0] + self[1], reverse=True)) # type:ignore def antisymmetric_part(self) -> list: r""" @@ -508,8 +508,9 @@ def shape_circled_diagram(self) -> Partition: sage: SuperPartition([[2,1,0],[3,3]]).shape_circled_diagram() [3, 3, 3, 2, 1] """ - return Partition(sorted([a + 1 for a in self.antisymmetric_part()] - + self.symmetric_part(), reverse=True)) + pi = sorted([a + 1 for a in self.antisymmetric_part()] + + self.symmetric_part(), reverse=True) + return Partition(pi) # type:ignore @staticmethod def from_circled_diagram(shape, corners) -> SuperPartition: @@ -539,9 +540,10 @@ def from_circled_diagram(shape, corners) -> SuperPartition: sage: all(sp == from_cd(*sp.to_circled_diagram()) for sp in SuperPartitions(4)) True """ - return SuperPartition([sorted([c[1] for c in corners], reverse=True), - [shape[i] for i in range(len(shape)) - if i not in [c[0] for c in corners]]]) + data = [sorted([c[1] for c in corners], reverse=True), + [shape[i] for i in range(len(shape)) + if i not in [c[0] for c in corners]]] + return SuperPartition(data) # type: ignore def to_circled_diagram(self) -> list: r""" @@ -611,7 +613,7 @@ def zee(self) -> Integer: sage: sum(1/sp.zee() for sp in SuperPartitions(6,0)) 1 """ - return Partition(self.symmetric_part()).centralizer_size() + return Partition(self.symmetric_part()).centralizer_size() # type:ignore def sign(self) -> int: r""" @@ -755,7 +757,7 @@ def add_horizontal_border_strip_star_bar(self, h) -> list: for asp in nsp: asp = asp + [0] change_in_rows = [asp[i] - sp1[i] for i in range(len(sp1))] - moved_circ_list = [[] for i in range(len(circ_list))] + moved_circ_list: list[list[tuple]] = [[] for _ in range(len(circ_list))] for i, pos in enumerate(circ_list): if change_in_rows[pos[0]] == 0: moved_circ_list[i].append(pos) diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index b0b8bd96253..2f7a379a747 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -51,7 +51,7 @@ from sage.arith.all import gcd -def paths_in_triangle(i, j, a, b) -> list: +def paths_in_triangle(i, j, a, b) -> list[tuple]: r""" Return all Dyck paths from `(0,0)` to `(i,j)` in the `(a \times b)`-rectangle. @@ -160,7 +160,7 @@ def swap(p, i, m=1) -> tuple: return tuple(q) -def GeneralizedTamariLattice(a, b, m=1, check=True) -> LatticePoset: +def GeneralizedTamariLattice(a, b, m=1, check=True): r""" Return the `(a,b)`-Tamari lattice of parameter `m`. @@ -227,7 +227,7 @@ def covers(p): for p in paths_in_triangle(a, b, a, b)}, check=check) -def TamariLattice(n, m=1) -> LatticePoset: +def TamariLattice(n, m=1): r""" Return the `n`-th Tamari lattice. @@ -340,7 +340,7 @@ def swap_dexter(p, i) -> list: return resu -def DexterSemilattice(n) -> MeetSemilattice: +def DexterSemilattice(n): r""" Return the `n`-th Dexter meet-semilattice. From 3a66b6a1c6dd03e045d6d38a9c6ad278a003e425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Oct 2021 16:43:14 +0200 Subject: [PATCH 097/122] detail --- src/sage/combinat/dyck_word.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index a3c80c12115..a98a29f2e26 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -2033,7 +2033,7 @@ def to_pair_of_standard_tableaux(self) -> tuple: return (T, T) else: left: list[list[int]] = [[], []] - right:list[list[int]] = [[], []] + right: list[list[int]] = [[], []] for pos in range(n): if self[pos] == open_symbol: left[0].append(pos + 1) From f01ff1f0f076fe550ffb6a6a657c5fda9dc0a023 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 20 Oct 2021 20:37:12 -0400 Subject: [PATCH 098/122] Trac #29024: obtain LIBSINGULAR_PATH from sage.env instead of sage_conf. On systems without sage_conf, this should keep things more or less working. The variable defaults to "libSingular.so" in the absence of divine intervention, a value that works on Linux at least. --- src/sage/env.py | 6 ++++++ src/sage/libs/singular/singular.pyx | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/env.py b/src/sage/env.py index 876c56d742d..e5af0374a2f 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -227,6 +227,12 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st NTL_LIBDIR = var("NTL_LIBDIR") LIE_INFO_DIR = var("LIE_INFO_DIR", join(SAGE_LOCAL, "lib", "LiE")) +# The path to libSingular, to be passed to dlopen(). This will +# typically be set to an absolute path in sage_conf, but the relative +# fallback path here works on systems where dlopen() searches the +# system's library locations. +LIBSINGULAR_PATH = var("LIBSINGULAR_PATH", "libSingular.so") + # OpenMP OPENMP_CFLAGS = var("OPENMP_CFLAGS", "") OPENMP_CXXFLAGS = var("OPENMP_CXXFLAGS", "") diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index ea6d0ab01d1..9eff1838d54 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -768,7 +768,7 @@ cdef init_libsingular(): cdef void *handle = NULL - from sage_conf import LIBSINGULAR_PATH + from sage.env import LIBSINGULAR_PATH lib = str_to_bytes(LIBSINGULAR_PATH, FS_ENCODING, "surrogateescape") # This is a workaround for https://github.com/Singular/Singular/issues/1113 From ae29c47bab2871fe740c66a802eee5c69a722074 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 21 Oct 2021 13:58:47 -0700 Subject: [PATCH 099/122] src/sage/rings/integer.pyx: Flush output in doctest --- src/sage/rings/integer.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 56de0fc858a..b24923fba6c 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -6432,7 +6432,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): TESTS:: sage: try: - ....: print('Possible error output from gmp') + ....: print('Possible error output from gmp', flush=True) ....: 1 << (2^60) ....: except (MemoryError, OverflowError, RuntimeError): ....: pass From 071121cd44319b780d1ef7c86721b701b74ad4a3 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Mon, 20 Sep 2021 08:51:25 +0200 Subject: [PATCH 100/122] add colored Jones polynomial to braids --- src/doc/en/reference/references/index.rst | 3 + src/sage/groups/braid.py | 421 ++++++++++++++++++++++ 2 files changed, 424 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index cecda8a5ef3..8a7e133e0ae 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2871,6 +2871,9 @@ REFERENCES: for odd modular subgroups", LMS J. Comput. Math. 17 (2014), no. 1, 206-208, :doi:`10.1112/S1461157013000338`. +.. [HL2018] Mustafa Hajij and Jesse Levitt, "An Efficient Algorithm to Compute + the Colored Jones Polynomial" :arxiv:`1804.07910v2`. + .. [Hli2006] Petr Hlineny, "Equivalence-free exhaustive generation of matroid representations", Discrete Applied Mathematics 154 (2006), pp. 1210-1222. diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 113bf59b2c3..6a13d8cc68d 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -53,6 +53,7 @@ problem to ensure correct Cayley graph computations. - Sebastian Oehms (July and Nov 2018): add other versions for burau_matrix (unitary + simple, see :trac:`25760` and :trac:`26657`) +- Moritz Firsching (Sept 2021): Colored Jones polynomial """ ############################################################################## @@ -65,23 +66,31 @@ # https://www.gnu.org/licenses/ ############################################################################## +import itertools +import collections +from operator import index from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import lazy_import from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod +from sage.algebras.free_algebra import FreeAlgebra from sage.categories.groups import Groups from sage.groups.free_group import FreeGroup, is_FreeGroup from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.matrix.constructor import identity_matrix, matrix from sage.combinat.permutation import Permutations +from sage.combinat.subset import Subsets from sage.categories.action import Action +from sage.knots.knot import Knot from sage.sets.set import Set from sage.groups.finitely_presented import FinitelyPresentedGroup from sage.groups.artin import FiniteTypeArtinGroup, FiniteTypeArtinGroupElement from sage.structure.richcmp import richcmp, rich_to_bool from sage.features import PythonModule + lazy_import('sage.libs.braiding', ['rightnormalform', 'centralizer', 'supersummitset', 'greatestcommondivisor', 'leastcommonmultiple', 'conjugatingbraid', 'ultrasummitset', @@ -1736,6 +1745,415 @@ def reverse(self): t.reverse() return self.parent()(tuple(t)) + class _RightQuantumWord: + def __init__(self, words): + r""" + An internal class representing right quantum words as in + definition 4.1 of [HL2018]_. + + INPUT: + - ``words`` -- An element in a suitable free algebra over a + Laurent polynomial ring in one variable. This input does not + need to be in reduced form, but the monomials for the input + can come in any order. + + EXAMPLES:: + sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = figure_8._deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: figure_8._RightQuantumWord(ap_1*cp_1 + + ....: q**3*bm_2*bp_1*am_0*cm_0) + The right quantum word represented by q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 reduced from ap_1*cp_1 + q^3*bm_2*bp_1*am_0*cm_0 + """ + self._Alg = words.parent() + self.q = self._Alg.base_ring().gen() + self.R = self._Alg.base_ring() + self._unreduced_words = words + self._tuples = None + self._gens = self._Alg.gens() + self._minus_begin = min((i for i, gen in enumerate(self._gens) if + 'm' in str(gen)), default=len(self._gens)) + + def as_tuples(self): + r""" + Get a representation of the right quantum word as a dict, with + keys monomials in the free algebra represented as tuples and + values in elements the Laurent polynomial ring in one variable. + + This is in the reduced form as outlines in definition of 4.1 of + [HL2018]_. + + OUTPUT: + A dict of tuples of ints corresponding to the exponents in the + generators self._gens(), with values in self.R. + + EXAMPLES:: + sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = figure_8._deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: qw = figure_8._RightQuantumWord(ap_1*cp_1 + + ....: q**3*bm_2*bp_1*am_0*cm_0) + sage: for key, value in qw.as_tuples().items(): + ....: print(key, value) + ....: + (0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0) q + (1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0) q^2 + """ + if self._tuples: + return self._tuples + self._tuples = collections.defaultdict(self.R) + for unreduced_monom, q_power in list(self._unreduced_words): + q = self.q + ret_tuple = [0] * len(self._gens) + for gen, exp in unreduced_monom: + gen_index = self._gens.index(gen) + is_minus = gen_index >= self._minus_begin + is_a = not(bool((gen_index+1) % 3)) + is_b = not(bool(gen_index % 3)) + is_c = not(bool((gen_index+2) % 3)) + index = gen_index//3 + # This uses the relations in equations (4.1) and (4.2) + # of [HL2018]_. + i, j, k = ret_tuple[3*index: 3*index + 3] + if is_a: + ret_tuple[3*index: 3*index + 3] = [i, j, k + exp] + if is_b: + ret_tuple[3*index: 3*index + 3] = [i + exp, j, k] + q_power *= q**(2*(k*exp + j*exp)) if is_minus \ + else q**(-2*j*exp) + if is_c: + ret_tuple[3*index: 3*index + 3] = [i, j + exp, k] + q_power *= q**(-k*exp) if is_minus else q**(k*exp) + self._tuples[tuple(ret_tuple)] += self._Alg(q_power) + return self._tuples + + def reduced_word(self): + r""" + Return the (reduced) right quantum word. + + OUTPUT: + An element in the free algebra self._Alg. + + EXAMPLES:: + sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = figure_8._deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: qw = figure_8._RightQuantumWord(ap_1*cp_1 + + ....: q**3*bm_2*bp_1*am_0*cm_0) + sage: qw.reduced_word() + q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 + + TESTS:: + + Testing the equations (4.1) and (4.2) in [HL2018]_. + + sage: figure_8._RightQuantumWord(ap_3*bp_3).reduced_word() + bp_3*ap_3 + sage: figure_8._RightQuantumWord(ap_3*cp_3).reduced_word() + q*cp_3*ap_3 + sage: figure_8._RightQuantumWord(cp_3*bp_3).reduced_word() + (q^-2)*bp_3*cp_3 + sage: figure_8._RightQuantumWord(am_2*bm_2).reduced_word() + q^2*bm_2*am_2 + sage: figure_8._RightQuantumWord(am_2*cm_2).reduced_word() + (q^-1)*cm_2*am_2 + sage: figure_8._RightQuantumWord(cm_2*bm_2).reduced_word() + q^2*bm_2*cm_2 + + .. TODO:: + Paralellize this function, calculating all summands in the sum + in parallel. + """ + def tuple_to_word(q_tuple): + return prod(self._gens[i]**exp + for i, exp in enumerate(q_tuple)) + return sum(q_factor*tuple_to_word(q_tuple) + for q_tuple, q_factor in self.as_tuples().items()) + + def eps(self, N): + r"""Evaluate the map $\mathcal{E}_N$ for a braid. + + INPUT: + - ``N`` -- an integer; the number of colors. + + EXAMPLES:: + + sage: B = BraidGroup(3) + sage: b = B([1,-2,1,2]) + sage: db = b._deformed_burau_matrix()[:, :] + sage: q = db.parent().base_ring().base_ring().gen() + sage: (bp_0, cp_0, ap_0, + ....: bp_2, cp_2, ap_2, + ....: bp_3, cp_3, ap_3, + ....: bm_1, cm_1, am_1) = db.parent().base_ring().gens() + sage: rqw = b._RightQuantumWord( + ....: q^3*bp_2*bp_0*ap_0 + q*ap_3*bm_1*am_1*bp_0) + sage: rqw.eps(3) + -(q^-1-2*q+q^5) + sage: rqw.eps(2) + -(1-2*q+q^2-q^3+q^4) + + TESTS:: + + sage: rqw.eps(1) + 0 + + .. TODO:: + Paralellize this function, calculating all summands in the sum + in parallel. + """ + def eps_monom(q_tuple): + q = self.q + r"""Evaluate the map $\mathcal{E}_N$ for a single mononial.""" + ret_q = q**sum((N - 1 - q_tuple[3*i + 2])*q_tuple[3*i + 1] + for i in range(self._minus_begin//3)) + ret_q *= q**sum((N - 1)*(-q_tuple[rj]) + for rj in range(self._minus_begin + 1, + len(q_tuple), 3)) + ret_q *= prod(prod(1 - q**(N - 1 - q_tuple[3*i + 1] - h) + for h in range(0, q_tuple[3*i + 2])) + for i in range(self._minus_begin//3)) + ret_q *= prod(prod(1 - q**(q_tuple[3*j + 1] + l + 1 - N) + for l in range(q_tuple[3*j + 2])) + for j in range(self._minus_begin//3, + len(q_tuple)//3)) + return ret_q + + return sum(q_factor*eps_monom(q_tuple) + for q_tuple, q_factor in self.as_tuples().items()) + + def __repr__(self): + r""" + String representation of the reight quantum word. + + EXAMPLES:: + sage: b = BraidGroup(3)([1,2,-1,2,-1]) + sage: db = b._deformed_burau_matrix(); db[2,2] + cp_1*am_2*bp_3 + sage: b._RightQuantumWord(db[2,2]) + The right quantum word represented by cp_1*bp_3*am_2 reduced from cp_1*am_2*bp_3 + """ + return 'The right quantum word represented by ' + \ + f'{str(self.reduced_word())} reduced from ' + \ + f'{str(self._unreduced_words)}' + + def _deformed_burau_matrix(self, variab='q'): + r""" + Return the deformed Burau matrix of the braid. + + INPUT: + - ``variab`` -- variable (default: ``q``); the variable in the + resulting laurent polynomial, which is the base ring for the + free algebra constructed. + + OUTPUT: + A matrix with elements in the free algebra `self._Alg`. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: b = B([1, 2, -3, -2, 3, 1]) + sage: db = b._deformed_burau_matrix(); db + [ ap_0*ap_5 ap_0*bp_5 bp_0*bp_1*cm_2*cp_4 + bp_0*ap_1*cm_3*ap_4 bp_0*ap_1*cm_3*bp_4] + [ cp_0*ap_5 cp_0*bp_5 0 0] + [ 0 0 cp_1*cm_3*ap_4 cp_1*cm_3*bp_4] + [ bm_2*bm_3*cp_5 0 am_2*cp_4 + bm_2*am_3*ap_4 bm_2*am_3*bp_4] + + We check how this relates to the nondeformed Burau matrix: + sage: def subs_gen(gen, q): + ....: gen_str = str(gen) + ....: v = q if 'p' in gen_str else 1/q + ....: if 'b' in gen_str: + ....: return v + ....: elif 'a' in gen_str: + ....: return 1 - v + ....: else: + ....: return 1 + sage: q = db.parent().base_ring().base_ring().gen() + sage: db_simp = db.subs({gen: subs_gen(gen, q) for gen in db.parent().base_ring().gens()}) + sage: db_simp + [ (1-2*q+q^2) (q-q^2) (q-q^2+q^3) (q^2-q^3)] + [ (1-q) q 0 0] + [ 0 0 (1-q) q] + [ (q^-2) 0 -(q^-2-q^-1) -(q^-1-1)] + sage: burau = b.burau_matrix(); burau + [1 - 2*t + t^2 t - t^2 t - t^2 + t^3 t^2 - t^3] + [ 1 - t t 0 0] + [ 0 0 1 - t t] + [ t^-2 0 -t^-2 + t^-1 -t^-1 + 1] + sage: t = burau.parent().base_ring().gen() + sage: burau.subs({t:q}).change_ring(db.parent().base_ring()) == db_simp + True + """ + R = LaurentPolynomialRing(IntegerRing(), variab) + n = self.strands() + m = len(self.Tietze()) + Algebra = FreeAlgebra(R, m*3, [f'{s}p_{i}' + for i in range(m) for s in 'bca' + if self.Tietze()[i] > 0] + [f'{s}m_{i}' + for i in range(m) for s in 'bca' + if self.Tietze()[i] < 0]) + gen_indizes = [i for i in range(m) if self.Tietze()[i] > 0] + \ + [i for i in range(m) if self.Tietze()[i] < 0] + + M = identity_matrix(Algebra, n) + for k, i in enumerate(self.Tietze()): + A = identity_matrix(Algebra, n) + gen_index = gen_indizes.index(k) + b, c, a = Algebra.gens()[3*gen_index:3*gen_index+3] + if i > 0: + A[i-1, i-1] = a + A[i, i] = 0 + A[i, i-1] = c + A[i-1, i] = b + if i < 0: + A[-1-i, -1-i] = 0 + A[-i, -i] = a + A[-1-i, -i] = c + A[-i, -1-i] = b + M = M * A + return M + + def _quantum_determinant(self, A, q): + r""" + Return the quantum deteminant of a matrix. + + INPUT: + - ``A`` -- a square matrix + - ``q`` -- a symbolic variable or a generator for a + Laurent polynomial ring. + + EXAMPLES:: + + sage: b = BraidGroup(2)([1,1,1]) + sage: A = Matrix([[SR(f'a{i}{j}') for i in range(2)] + ....: for j in range(2)]); A + [a00 a10] + [a01 a11] + sage: b._quantum_determinant(A, SR('q')) + -a01*a10*q + a00*a11 + sage: A = Matrix([[SR(f'a{i}{j}') for i in range(3)] + ....: for j in range(3)]); A + [a00 a10 a20] + [a01 a11 a21] + [a02 a12 a22] + sage: b._quantum_determinant(A, SR('q')) + -a02*a11*a20*q^3 + a01*a12*a20*q^2 + a02*a10*a21*q^2 - a00*a12*a21*q - a01*a10*a22*q + a00*a11*a22 + """ + # We assume a square matrix as input + n = A.ncols() + return sum((-q)**(s.number_of_inversions()) * + prod(A[s(i + 1) - 1, i] for i in range(n)) + for s in Permutations(n)) + + def _colored_jones_sum(self, N, qword): + r"""Helper function to get the colored Jones polynomial. + + INPUT: + - ``N`` -- An integer; the number of colors. + - ``qword`` -- A right quantum word (possibly in unreduced form). + + EXAMPLES:: + + sage: b = BraidGroup(2)([1,1,1]) + sage: db = b._deformed_burau_matrix()[1:,1:]; db + [cp_0*ap_1*bp_2] + sage: b._colored_jones_sum(2, db[0,0]) + (1+q-q^2) + sage: b._colored_jones_sum(3, db[0,0]) + (1+q^2-q^5-q^6+q^7) + sage: b._colored_jones_sum(4, db[0,0]) + (1+q^3-q^8-q^10+q^13+q^14-q^15) + """ + rqword = self._RightQuantumWord(qword).reduced_word() + alg = qword.parent() + R = alg.base_ring() + result = R(1) + current_word = alg(1) + # This seemingly infinite sum is always finite if the qword comes + # from a sum of quantum determinants; because at some point + # the break condition will become true. + for i in itertools.count(1): + current_word *= rqword + new_rqw = self._RightQuantumWord(alg(current_word)) + current_word = new_rqw.reduced_word() + if not (new_eps := new_rqw.eps(N)): + break + result += new_eps + return result + + @cached_method + def colored_jones_polynomial(self, N, variab='q', try_inverse=True): + r""" + Return the colored Jones polynomial of the trace closure of the braid. + + INPUT: + - ``N`` -- integer; the number of colors. + - ``variab`` -- string (default: ``q``); the variable in the + resulting laurent polynomial. + - ``try_inverse`` -- boolean (default: ``True``); if ``True``, + attempt a faster calculation by using the inverse of the braid. + + EXAMPLES:: + + sage: trefoil = BraidGroup(2)([1,1,1]) + sage: trefoil.colored_jones_polynomial(2) + q + q^3 - q^4 + sage: trefoil.colored_jones_polynomial(4) + q^3 + q^7 - q^10 + q^11 - q^13 - q^14 + q^15 - q^17 + q^19 + q^20 - q^21 + sage: trefoil.inverse().colored_jones_polynomial(4) + -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - q^-10 + q^-7 + q^-3 + + sage: figure_eight = BraidGroup(3)([-1, 2, -1, 2]) + sage: figure_eight.colored_jones_polynomial(2) + q^-2 - q^-1 + 1 - q + q^2 + sage: figure_eight.colored_jones_polynomial(3, 'Q') + Q^-6 - Q^-5 - Q^-4 + 2*Q^-3 - Q^-2 - Q^-1 + 3 - Q - Q^2 + 2*Q^3 - Q^4 - Q^5 + Q^6 + + ALGORITHM: + + The algorithm used is described in [HL2018]_. We follow their + notation, but work in a suitable free algebra over a Laurent + polynomial ring in one variable to simplify bookkeeping. + """ + if self.components_in_closure() != 1: + raise ValueError("the number of components must be 1") + db = self._deformed_burau_matrix(variab)[1:, 1:] + q = db.parent().base_ring().base_ring().gen() + n = db.ncols() + qword = sum((-1)**(s.cardinality() - 1)*self._quantum_determinant( + q*db[list(s), list(s)], q) for s in Subsets(range(n)) if s) + inverse_shorter = try_inverse + if try_inverse: + db_inv = self.inverse()._deformed_burau_matrix(variab)[1:, 1:] + q_inv = db_inv.parent().base_ring().base_ring().gen() + qword_inv = sum((-1)**(s.cardinality() - + 1)*self._quantum_determinant(q*db_inv[list(s), + list(s)], q) + for s in Subsets(range(n)) if s) + # Check if the inverse has a shorter expression at this point + inverse_shorter = len(list(qword_inv)) < len(list(qword)) + use_inverse = try_inverse and inverse_shorter + shorter_qword = qword_inv if use_inverse else qword + knot = Knot(self.inverse()) if use_inverse else Knot(self) + cj = q**(((N - 1)*(knot.writhe() - self.strands() + 1))/2) * \ + self._colored_jones_sum(N, shorter_qword).leading_coefficient() + return cj.subs({q: 1/q}) if use_inverse else cj class BraidGroup_class(FiniteTypeArtinGroup): """ @@ -2514,6 +2932,7 @@ def _element_from_libbraiding(self, nf): from sage.misc.misc_c import prod return self.delta() ** nf[0][0] * prod(self(i) for i in nf[1:]) +<<<<<<< HEAD def mirror_involution(self): r""" Return the mirror involution of ``self``. @@ -2540,6 +2959,8 @@ def mirror_involution(self): return self.hom(gens_mirr, check=False) +======= +>>>>>>> 6e95c3a616 (add colored Jones polynomial to braids) def BraidGroup(n=None, names='s'): """ Construct a Braid Group From 775c29d307cfed4175ca3d8033a0ca9be60ad3cb Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Wed, 29 Sep 2021 08:36:10 +0200 Subject: [PATCH 101/122] remove walrus --- src/sage/groups/braid.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 6a13d8cc68d..fa7e60c4a9d 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2092,7 +2092,8 @@ def _colored_jones_sum(self, N, qword): current_word *= rqword new_rqw = self._RightQuantumWord(alg(current_word)) current_word = new_rqw.reduced_word() - if not (new_eps := new_rqw.eps(N)): + new_eps = new_rqw.eps(N) + if not (new_eps): break result += new_eps return result From 975ab638731f311f609ecadc46fcba0a6701b68b Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Wed, 29 Sep 2021 15:50:56 +0200 Subject: [PATCH 102/122] use q_inv instead of q for clarity --- src/sage/groups/braid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index fa7e60c4a9d..949fd58dd37 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2144,7 +2144,7 @@ def colored_jones_polynomial(self, N, variab='q', try_inverse=True): db_inv = self.inverse()._deformed_burau_matrix(variab)[1:, 1:] q_inv = db_inv.parent().base_ring().base_ring().gen() qword_inv = sum((-1)**(s.cardinality() - - 1)*self._quantum_determinant(q*db_inv[list(s), + 1)*self._quantum_determinant(q_inv*db_inv[list(s), list(s)], q) for s in Subsets(range(n)) if s) # Check if the inverse has a shorter expression at this point From bf8a4ffd041881661a7fa0c506960c49ef97fc5d Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Wed, 29 Sep 2021 21:48:28 +0200 Subject: [PATCH 103/122] add indentation for EXAMPLES --- src/sage/groups/braid.py | 89 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 949fd58dd37..a1c65638ee5 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -1758,6 +1758,7 @@ def __init__(self, words): can come in any order. EXAMPLES:: + sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) sage: ( ....: bp_1, cp_1, ap_1, @@ -1793,6 +1794,7 @@ def as_tuples(self): generators self._gens(), with values in self.R. EXAMPLES:: + sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) sage: ( ....: bp_1, cp_1, ap_1, @@ -1845,6 +1847,7 @@ def reduced_word(self): An element in the free algebra self._Alg. EXAMPLES:: + sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) sage: ( ....: bp_1, cp_1, ap_1, @@ -1893,20 +1896,20 @@ def eps(self, N): EXAMPLES:: - sage: B = BraidGroup(3) - sage: b = B([1,-2,1,2]) - sage: db = b._deformed_burau_matrix()[:, :] - sage: q = db.parent().base_ring().base_ring().gen() - sage: (bp_0, cp_0, ap_0, - ....: bp_2, cp_2, ap_2, - ....: bp_3, cp_3, ap_3, - ....: bm_1, cm_1, am_1) = db.parent().base_ring().gens() - sage: rqw = b._RightQuantumWord( - ....: q^3*bp_2*bp_0*ap_0 + q*ap_3*bm_1*am_1*bp_0) - sage: rqw.eps(3) - -(q^-1-2*q+q^5) - sage: rqw.eps(2) - -(1-2*q+q^2-q^3+q^4) + sage: B = BraidGroup(3) + sage: b = B([1,-2,1,2]) + sage: db = b._deformed_burau_matrix()[:, :] + sage: q = db.parent().base_ring().base_ring().gen() + sage: (bp_0, cp_0, ap_0, + ....: bp_2, cp_2, ap_2, + ....: bp_3, cp_3, ap_3, + ....: bm_1, cm_1, am_1) = db.parent().base_ring().gens() + sage: rqw = b._RightQuantumWord( + ....: q^3*bp_2*bp_0*ap_0 + q*ap_3*bm_1*am_1*bp_0) + sage: rqw.eps(3) + -(q^-1-2*q+q^5) + sage: rqw.eps(2) + -(1-2*q+q^2-q^3+q^4) TESTS:: @@ -1942,11 +1945,11 @@ def __repr__(self): String representation of the reight quantum word. EXAMPLES:: - sage: b = BraidGroup(3)([1,2,-1,2,-1]) - sage: db = b._deformed_burau_matrix(); db[2,2] - cp_1*am_2*bp_3 - sage: b._RightQuantumWord(db[2,2]) - The right quantum word represented by cp_1*bp_3*am_2 reduced from cp_1*am_2*bp_3 + sage: b = BraidGroup(3)([1,2,-1,2,-1]) + sage: db = b._deformed_burau_matrix(); db[2,2] + cp_1*am_2*bp_3 + sage: b._RightQuantumWord(db[2,2]) + The right quantum word represented by cp_1*bp_3*am_2 reduced from cp_1*am_2*bp_3 """ return 'The right quantum word represented by ' + \ f'{str(self.reduced_word())} reduced from ' + \ @@ -1974,7 +1977,7 @@ def _deformed_burau_matrix(self, variab='q'): [ 0 0 cp_1*cm_3*ap_4 cp_1*cm_3*bp_4] [ bm_2*bm_3*cp_5 0 am_2*cp_4 + bm_2*am_3*ap_4 bm_2*am_3*bp_4] - We check how this relates to the nondeformed Burau matrix: + We check how this relates to the nondeformed Burau matrix: sage: def subs_gen(gen, q): ....: gen_str = str(gen) ....: v = q if 'p' in gen_str else 1/q @@ -2040,20 +2043,20 @@ def _quantum_determinant(self, A, q): EXAMPLES:: - sage: b = BraidGroup(2)([1,1,1]) - sage: A = Matrix([[SR(f'a{i}{j}') for i in range(2)] - ....: for j in range(2)]); A - [a00 a10] - [a01 a11] - sage: b._quantum_determinant(A, SR('q')) - -a01*a10*q + a00*a11 - sage: A = Matrix([[SR(f'a{i}{j}') for i in range(3)] - ....: for j in range(3)]); A - [a00 a10 a20] - [a01 a11 a21] - [a02 a12 a22] - sage: b._quantum_determinant(A, SR('q')) - -a02*a11*a20*q^3 + a01*a12*a20*q^2 + a02*a10*a21*q^2 - a00*a12*a21*q - a01*a10*a22*q + a00*a11*a22 + sage: b = BraidGroup(2)([1,1,1]) + sage: A = Matrix([[SR(f'a{i}{j}') for i in range(2)] + ....: for j in range(2)]); A + [a00 a10] + [a01 a11] + sage: b._quantum_determinant(A, SR('q')) + -a01*a10*q + a00*a11 + sage: A = Matrix([[SR(f'a{i}{j}') for i in range(3)] + ....: for j in range(3)]); A + [a00 a10 a20] + [a01 a11 a21] + [a02 a12 a22] + sage: b._quantum_determinant(A, SR('q')) + -a02*a11*a20*q^3 + a01*a12*a20*q^2 + a02*a10*a21*q^2 - a00*a12*a21*q - a01*a10*a22*q + a00*a11*a22 """ # We assume a square matrix as input n = A.ncols() @@ -2070,15 +2073,15 @@ def _colored_jones_sum(self, N, qword): EXAMPLES:: - sage: b = BraidGroup(2)([1,1,1]) - sage: db = b._deformed_burau_matrix()[1:,1:]; db - [cp_0*ap_1*bp_2] - sage: b._colored_jones_sum(2, db[0,0]) - (1+q-q^2) - sage: b._colored_jones_sum(3, db[0,0]) - (1+q^2-q^5-q^6+q^7) - sage: b._colored_jones_sum(4, db[0,0]) - (1+q^3-q^8-q^10+q^13+q^14-q^15) + sage: b = BraidGroup(2)([1,1,1]) + sage: db = b._deformed_burau_matrix()[1:,1:]; db + [cp_0*ap_1*bp_2] + sage: b._colored_jones_sum(2, db[0,0]) + (1+q-q^2) + sage: b._colored_jones_sum(3, db[0,0]) + (1+q^2-q^5-q^6+q^7) + sage: b._colored_jones_sum(4, db[0,0]) + (1+q^3-q^8-q^10+q^13+q^14-q^15) """ rqword = self._RightQuantumWord(qword).reduced_word() alg = qword.parent() From b0b1d8beef5aecc791f61f9987cf87509b7ae96e Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Wed, 29 Sep 2021 21:53:58 +0200 Subject: [PATCH 104/122] remove unused index --- src/sage/groups/braid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index a1c65638ee5..d8e2ec1c28b 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -68,7 +68,6 @@ import itertools import collections -from operator import index from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.misc.lazy_attribute import lazy_attribute From c01f408e147ad07aa076b9f61b22b357eb89934f Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Tue, 5 Oct 2021 13:27:07 +0200 Subject: [PATCH 105/122] first round of review comments --- src/sage/groups/braid.py | 358 +++++++++++++++++++++++++++++++-------- 1 file changed, 284 insertions(+), 74 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index d8e2ec1c28b..a4ede96bf0d 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -66,7 +66,6 @@ # https://www.gnu.org/licenses/ ############################################################################## -import itertools import collections from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing @@ -1705,6 +1704,7 @@ def sliding_circuits(self): B = self.parent() return [[B._element_from_libbraiding(i) for i in s] for s in slc] +<<<<<<< HEAD def mirror_image(self): r""" Return the image of ``self`` under the mirror involution (see @@ -1955,26 +1955,30 @@ def __repr__(self): f'{str(self._unreduced_words)}' def _deformed_burau_matrix(self, variab='q'): +======= + def deformed_burau_matrix(self, variab='q'): +>>>>>>> 062a9e9a44 (first round of review comments) r""" Return the deformed Burau matrix of the braid. INPUT: + - ``variab`` -- variable (default: ``q``); the variable in the resulting laurent polynomial, which is the base ring for the free algebra constructed. OUTPUT: - A matrix with elements in the free algebra `self._Alg`. + + A matrix with elements in the free algebra `self._Alg`. EXAMPLES:: sage: B = BraidGroup(4) sage: b = B([1, 2, -3, -2, 3, 1]) - sage: db = b._deformed_burau_matrix(); db - [ ap_0*ap_5 ap_0*bp_5 bp_0*bp_1*cm_2*cp_4 + bp_0*ap_1*cm_3*ap_4 bp_0*ap_1*cm_3*bp_4] - [ cp_0*ap_5 cp_0*bp_5 0 0] - [ 0 0 cp_1*cm_3*ap_4 cp_1*cm_3*bp_4] - [ bm_2*bm_3*cp_5 0 am_2*cp_4 + bm_2*am_3*ap_4 bm_2*am_3*bp_4] + sage: db = b.deformed_burau_matrix(); db + [ ap_0*ap_5 ... bp_0*ap_1*cm_3*bp_4] + ... + [ bm_2*bm_3*cp_5 ... bm_2*am_3*bp_4] We check how this relates to the nondeformed Burau matrix: sage: def subs_gen(gen, q): @@ -1986,8 +1990,10 @@ def _deformed_burau_matrix(self, variab='q'): ....: return 1 - v ....: else: ....: return 1 - sage: q = db.parent().base_ring().base_ring().gen() - sage: db_simp = db.subs({gen: subs_gen(gen, q) for gen in db.parent().base_ring().gens()}) + sage: db_base = db.parent().base_ring() + sage: q = db_base.base_ring().gen() + sage: db_simp = db.subs({gen: subs_gen(gen, q) + ....: for gen in db_base.gens()}) sage: db_simp [ (1-2*q+q^2) (q-q^2) (q-q^2+q^3) (q^2-q^3)] [ (1-q) q 0 0] @@ -1999,7 +2005,7 @@ def _deformed_burau_matrix(self, variab='q'): [ 0 0 1 - t t] [ t^-2 0 -t^-2 + t^-1 -t^-1 + 1] sage: t = burau.parent().base_ring().gen() - sage: burau.subs({t:q}).change_ring(db.parent().base_ring()) == db_simp + sage: burau.subs({t:q}).change_ring(db_base) == db_simp True """ R = LaurentPolynomialRing(IntegerRing(), variab) @@ -2031,49 +2037,19 @@ def _deformed_burau_matrix(self, variab='q'): M = M * A return M - def _quantum_determinant(self, A, q): - r""" - Return the quantum deteminant of a matrix. - - INPUT: - - ``A`` -- a square matrix - - ``q`` -- a symbolic variable or a generator for a - Laurent polynomial ring. - - EXAMPLES:: - - sage: b = BraidGroup(2)([1,1,1]) - sage: A = Matrix([[SR(f'a{i}{j}') for i in range(2)] - ....: for j in range(2)]); A - [a00 a10] - [a01 a11] - sage: b._quantum_determinant(A, SR('q')) - -a01*a10*q + a00*a11 - sage: A = Matrix([[SR(f'a{i}{j}') for i in range(3)] - ....: for j in range(3)]); A - [a00 a10 a20] - [a01 a11 a21] - [a02 a12 a22] - sage: b._quantum_determinant(A, SR('q')) - -a02*a11*a20*q^3 + a01*a12*a20*q^2 + a02*a10*a21*q^2 - a00*a12*a21*q - a01*a10*a22*q + a00*a11*a22 - """ - # We assume a square matrix as input - n = A.ncols() - return sum((-q)**(s.number_of_inversions()) * - prod(A[s(i + 1) - 1, i] for i in range(n)) - for s in Permutations(n)) - + @cached_method def _colored_jones_sum(self, N, qword): r"""Helper function to get the colored Jones polynomial. INPUT: - - ``N`` -- An integer; the number of colors. - - ``qword`` -- A right quantum word (possibly in unreduced form). + + - ``N`` -- An integer; the number of colors. + - ``qword`` -- A right quantum word (possibly in unreduced form). EXAMPLES:: sage: b = BraidGroup(2)([1,1,1]) - sage: db = b._deformed_burau_matrix()[1:,1:]; db + sage: db = b.deformed_burau_matrix()[1:,1:]; db [cp_0*ap_1*bp_2] sage: b._colored_jones_sum(2, db[0,0]) (1+q-q^2) @@ -2082,35 +2058,38 @@ def _colored_jones_sum(self, N, qword): sage: b._colored_jones_sum(4, db[0,0]) (1+q^3-q^8-q^10+q^13+q^14-q^15) """ - rqword = self._RightQuantumWord(qword).reduced_word() + rqword = RightQuantumWord(qword).reduced_word() alg = qword.parent() R = alg.base_ring() result = R(1) current_word = alg(1) + i = 1 + continue_summing = True # This seemingly infinite sum is always finite if the qword comes # from a sum of quantum determinants; because at some point # the break condition will become true. - for i in itertools.count(1): + while continue_summing: current_word *= rqword - new_rqw = self._RightQuantumWord(alg(current_word)) + new_rqw = RightQuantumWord(alg(current_word)) current_word = new_rqw.reduced_word() new_eps = new_rqw.eps(N) - if not (new_eps): - break result += new_eps + if not (new_eps): + continue_summing = False + i += 1 return result - @cached_method - def colored_jones_polynomial(self, N, variab='q', try_inverse=True): + def colored_jones_polynomial(self, N, variab=None, try_inverse=True): r""" Return the colored Jones polynomial of the trace closure of the braid. INPUT: - - ``N`` -- integer; the number of colors. - - ``variab`` -- string (default: ``q``); the variable in the - resulting laurent polynomial. - - ``try_inverse`` -- boolean (default: ``True``); if ``True``, - attempt a faster calculation by using the inverse of the braid. + + - ``N`` -- integer; the number of colors. + - ``variab`` -- string (default: ``q``); the variable in the + resulting laurent polynomial. + - ``try_inverse`` -- boolean (default: ``True``); if ``True``, + attempt a faster calculation by using the inverse of the braid. EXAMPLES:: @@ -2118,36 +2097,40 @@ def colored_jones_polynomial(self, N, variab='q', try_inverse=True): sage: trefoil.colored_jones_polynomial(2) q + q^3 - q^4 sage: trefoil.colored_jones_polynomial(4) - q^3 + q^7 - q^10 + q^11 - q^13 - q^14 + q^15 - q^17 + q^19 + q^20 - q^21 + q^3 + q^7 - q^10 + q^11 - q^13 - q^14 + q^15 - q^17 + q^19 + q^20 + - q^21 sage: trefoil.inverse().colored_jones_polynomial(4) - -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - q^-10 + q^-7 + q^-3 + -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - + q^-10 + q^-7 + q^-3 sage: figure_eight = BraidGroup(3)([-1, 2, -1, 2]) sage: figure_eight.colored_jones_polynomial(2) q^-2 - q^-1 + 1 - q + q^2 sage: figure_eight.colored_jones_polynomial(3, 'Q') - Q^-6 - Q^-5 - Q^-4 + 2*Q^-3 - Q^-2 - Q^-1 + 3 - Q - Q^2 + 2*Q^3 - Q^4 - Q^5 + Q^6 + Q^-6 - Q^-5 - Q^-4 + 2*Q^-3 - Q^-2 - Q^-1 + 3 - Q - Q^2 + 2*Q^3 + - Q^4 - Q^5 + Q^6 ALGORITHM: - The algorithm used is described in [HL2018]_. We follow their - notation, but work in a suitable free algebra over a Laurent - polynomial ring in one variable to simplify bookkeeping. + The algorithm used is described in [HL2018]_. We follow their notation, + but work in a suitable free algebra over a Laurent polynomial ring in + one variable to simplify bookkeeping. """ if self.components_in_closure() != 1: raise ValueError("the number of components must be 1") - db = self._deformed_burau_matrix(variab)[1:, 1:] + db = self.deformed_burau_matrix('q')[1:, 1:] q = db.parent().base_ring().base_ring().gen() n = db.ncols() - qword = sum((-1)**(s.cardinality() - 1)*self._quantum_determinant( - q*db[list(s), list(s)], q) for s in Subsets(range(n)) if s) + qword = sum((-1)**(s.cardinality() - 1)*(q*db[list(s), + list(s)]).quantum_determinant(q) + for s in Subsets(range(n)) if s) inverse_shorter = try_inverse if try_inverse: - db_inv = self.inverse()._deformed_burau_matrix(variab)[1:, 1:] + db_inv = self.inverse().deformed_burau_matrix('q')[1:, 1:] q_inv = db_inv.parent().base_ring().base_ring().gen() qword_inv = sum((-1)**(s.cardinality() - - 1)*self._quantum_determinant(q_inv*db_inv[list(s), - list(s)], q) + 1)*(q_inv*db_inv[list(s), + list(s)]).quantum_determinant(q) for s in Subsets(range(n)) if s) # Check if the inverse has a shorter expression at this point inverse_shorter = len(list(qword_inv)) < len(list(qword)) @@ -2156,7 +2139,238 @@ def colored_jones_polynomial(self, N, variab='q', try_inverse=True): knot = Knot(self.inverse()) if use_inverse else Knot(self) cj = q**(((N - 1)*(knot.writhe() - self.strands() + 1))/2) * \ self._colored_jones_sum(N, shorter_qword).leading_coefficient() - return cj.subs({q: 1/q}) if use_inverse else cj + cj_with_q = cj.subs({q: 1/q}) if use_inverse else cj + + # Up to this point, we have calculated everyting with a variable named + # `q` instead of using `variab`, because this allows proper caching in + # `_colored_jones_sum`. Here we do the substitution as necessary. + if not variab: + return cj_with_q + new_q = LaurentPolynomialRing(IntegerRing(), variab).gen() + return cj_with_q.subs({q: new_q}) + + +class RightQuantumWord: + def __init__(self, words): + r""" + An internal class representing right quantum words as in + definition 4.1 of [HL2018]_. + + INPUT: + + - ``words`` -- An element in a suitable free algebra over a Laurent + polynomial ring in one variable. This input does not need to be in + reduced form, but the monomials for the input can come in any order. + + EXAMPLES:: + + sage: from sage.groups.braid import RightQuantumWord + sage: fig_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = fig_8.deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: RightQuantumWord(ap_1*cp_1 + q**3*bm_2*bp_1*am_0*cm_0) + The right quantum word represented by + q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 + reduced from ap_1*cp_1 + q^3*bm_2*bp_1*am_0*cm_0 + """ + self._Alg = words.parent() + self.q = self._Alg.base_ring().gen() + self.R = self._Alg.base_ring() + self._unreduced_words = words + self._tuples = None + self._gens = self._Alg.gens() + self._minus_begin = min((i for i, gen in enumerate(self._gens) if + 'm' in str(gen)), default=len(self._gens)) + + def as_tuples(self): + r""" + Get a representation of the right quantum word as a dict, with + keys monomials in the free algebra represented as tuples and + values in elements the Laurent polynomial ring in one variable. + + This is in the reduced form as outlines in definition of 4.1 of + [HL2018]_. + + OUTPUT: + + A dict of tuples of ints corresponding to the exponents in the + generators self._gens(), with values in self.R. + + EXAMPLES:: + + sage: from sage.groups.braid import RightQuantumWord + sage: fig_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = fig_8.deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: qw = RightQuantumWord(ap_1*cp_1 + + ....: q**3*bm_2*bp_1*am_0*cm_0) + sage: for key, value in qw.as_tuples().items(): + ....: print(key, value) + ....: + (0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0) q + (1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0) q^2 + """ + if self._tuples: + return self._tuples + self._tuples = collections.defaultdict(self.R) + for unreduced_monom, q_power in list(self._unreduced_words): + q = self.q + ret_tuple = [0] * len(self._gens) + for gen, exp in unreduced_monom: + gen_index = self._gens.index(gen) + is_minus = gen_index >= self._minus_begin + is_a = not(bool((gen_index+1) % 3)) + is_b = not(bool(gen_index % 3)) + is_c = not(bool((gen_index+2) % 3)) + index = gen_index//3 + # This uses the relations in equations (4.1) and (4.2) + # of [HL2018]_. + i, j, k = ret_tuple[3*index: 3*index + 3] + if is_a: + ret_tuple[3*index: 3*index + 3] = [i, j, k + exp] + if is_b: + ret_tuple[3*index: 3*index + 3] = [i + exp, j, k] + q_power *= q**(2*(k*exp + j*exp)) if is_minus \ + else q**(-2*j*exp) + if is_c: + ret_tuple[3*index: 3*index + 3] = [i, j + exp, k] + q_power *= q**(-k*exp) if is_minus else q**(k*exp) + self._tuples[tuple(ret_tuple)] += self._Alg(q_power) + return self._tuples + + def reduced_word(self): + r""" + Return the (reduced) right quantum word. + + OUTPUT: + + An element in the free algebra self._Alg. + + EXAMPLES:: + + sage: from sage.groups.braid import RightQuantumWord + sage: fig_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = fig_8.deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: qw = RightQuantumWord(ap_1*cp_1 + + ....: q**3*bm_2*bp_1*am_0*cm_0) + sage: qw.reduced_word() + q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 + + TESTS:: + + Testing the equations (4.1) and (4.2) in [HL2018]_. + + sage: RightQuantumWord(ap_3*bp_3).reduced_word() + bp_3*ap_3 + sage: RightQuantumWord(ap_3*cp_3).reduced_word() + q*cp_3*ap_3 + sage: RightQuantumWord(cp_3*bp_3).reduced_word() + (q^-2)*bp_3*cp_3 + sage: RightQuantumWord(am_2*bm_2).reduced_word() + q^2*bm_2*am_2 + sage: RightQuantumWord(am_2*cm_2).reduced_word() + (q^-1)*cm_2*am_2 + sage: RightQuantumWord(cm_2*bm_2).reduced_word() + q^2*bm_2*cm_2 + + .. TODO:: + Paralellize this function, calculating all summands in the sum + in parallel. + """ + def tuple_to_word(q_tuple): + return prod(self._gens[i]**exp + for i, exp in enumerate(q_tuple)) + return sum(q_factor*tuple_to_word(q_tuple) + for q_tuple, q_factor in self.as_tuples().items()) + + def eps(self, N): + r"""Evaluate the map $\mathcal{E}_N$ for a braid. + + INPUT: + + - ``N`` -- an integer; the number of colors. + + EXAMPLES:: + + sage: from sage.groups.braid import RightQuantumWord + sage: B = BraidGroup(3) + sage: b = B([1,-2,1,2]) + sage: db = b.deformed_burau_matrix()[:, :] + sage: q = db.parent().base_ring().base_ring().gen() + sage: (bp_0, cp_0, ap_0, + ....: bp_2, cp_2, ap_2, + ....: bp_3, cp_3, ap_3, + ....: bm_1, cm_1, am_1) = db.parent().base_ring().gens() + sage: rqw = RightQuantumWord( + ....: q^3*bp_2*bp_0*ap_0 + q*ap_3*bm_1*am_1*bp_0) + sage: rqw.eps(3) + -(q^-1-2*q+q^5) + sage: rqw.eps(2) + -(1-2*q+q^2-q^3+q^4) + + TESTS:: + + sage: rqw.eps(1) + 0 + + .. TODO:: + Paralellize this function, calculating all summands in the sum + in parallel. + """ + def eps_monom(q_tuple): + q = self.q + r"""Evaluate the map $\mathcal{E}_N$ for a single mononial.""" + ret_q = q**sum((N - 1 - q_tuple[3*i + 2])*q_tuple[3*i + 1] + for i in range(self._minus_begin//3)) + ret_q *= q**sum((N - 1)*(-q_tuple[rj]) + for rj in range(self._minus_begin + 1, + len(q_tuple), 3)) + ret_q *= prod(prod(1 - q**(N - 1 - q_tuple[3*i + 1] - h) + for h in range(0, q_tuple[3*i + 2])) + for i in range(self._minus_begin//3)) + ret_q *= prod(prod(1 - q**(q_tuple[3*j + 1] + l + 1 - N) + for l in range(q_tuple[3*j + 2])) + for j in range(self._minus_begin//3, + len(q_tuple)//3)) + return ret_q + + return sum(q_factor*eps_monom(q_tuple) + for q_tuple, q_factor in self.as_tuples().items()) + + def __repr__(self): + r""" + String representation of the reight quantum word. + + EXAMPLES:: + + sage: from sage.groups.braid import RightQuantumWord + sage: b = BraidGroup(3)([1,2,-1,2,-1]) + sage: db = b.deformed_burau_matrix(); db[2,2] + cp_1*am_2*bp_3 + sage: RightQuantumWord(db[2,2]) + The right quantum word represented by cp_1*bp_3*am_2 reduced from + cp_1*am_2*bp_3 + """ + return 'The right quantum word represented by ' + \ + f'{str(self.reduced_word())} reduced from ' + \ + f'{str(self._unreduced_words)}' + class BraidGroup_class(FiniteTypeArtinGroup): """ @@ -2935,7 +3149,6 @@ def _element_from_libbraiding(self, nf): from sage.misc.misc_c import prod return self.delta() ** nf[0][0] * prod(self(i) for i in nf[1:]) -<<<<<<< HEAD def mirror_involution(self): r""" Return the mirror involution of ``self``. @@ -2961,9 +3174,6 @@ def mirror_involution(self): gens_mirr = [~g for g in self.gens()] return self.hom(gens_mirr, check=False) - -======= ->>>>>>> 6e95c3a616 (add colored Jones polynomial to braids) def BraidGroup(n=None, names='s'): """ Construct a Braid Group From eca48699f69049ab305d3c42ca4fd49f2cd49399 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Wed, 6 Oct 2021 08:26:52 +0200 Subject: [PATCH 106/122] fix doc --- src/sage/groups/braid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index a4ede96bf0d..171bba7d5b1 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2159,8 +2159,8 @@ def __init__(self, words): INPUT: - ``words`` -- An element in a suitable free algebra over a Laurent - polynomial ring in one variable. This input does not need to be in - reduced form, but the monomials for the input can come in any order. + polynomial ring in one variable. This input does not need to be in + reduced form, but the monomials for the input can come in any order. EXAMPLES:: From 022fcde5e2c483d08300fedef34165df0ff7de21 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 8 Oct 2021 10:33:20 +0900 Subject: [PATCH 107/122] Reviewer changes to colored Jones polynomial. --- src/sage/groups/braid.py | 237 +++++++++++++++++++++------------------ 1 file changed, 127 insertions(+), 110 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 171bba7d5b1..fe55c7d235a 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -66,7 +66,6 @@ # https://www.gnu.org/licenses/ ############################################################################## -import collections from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.misc.lazy_attribute import lazy_attribute @@ -1965,11 +1964,11 @@ def deformed_burau_matrix(self, variab='q'): - ``variab`` -- variable (default: ``q``); the variable in the resulting laurent polynomial, which is the base ring for the - free algebra constructed. + free algebra constructed OUTPUT: - A matrix with elements in the free algebra `self._Alg`. + A matrix with elements in the free algebra `self._algebra`. EXAMPLES:: @@ -1980,7 +1979,8 @@ def deformed_burau_matrix(self, variab='q'): ... [ bm_2*bm_3*cp_5 ... bm_2*am_3*bp_4] - We check how this relates to the nondeformed Burau matrix: + We check how this relates to the nondeformed Burau matrix:: + sage: def subs_gen(gen, q): ....: gen_str = str(gen) ....: v = q if 'p' in gen_str else 1/q @@ -1993,7 +1993,7 @@ def deformed_burau_matrix(self, variab='q'): sage: db_base = db.parent().base_ring() sage: q = db_base.base_ring().gen() sage: db_simp = db.subs({gen: subs_gen(gen, q) - ....: for gen in db_base.gens()}) + ....: for gen in db_base.gens()}) sage: db_simp [ (1-2*q+q^2) (q-q^2) (q-q^2+q^3) (q^2-q^3)] [ (1-q) q 0 0] @@ -2011,19 +2011,20 @@ def deformed_burau_matrix(self, variab='q'): R = LaurentPolynomialRing(IntegerRing(), variab) n = self.strands() m = len(self.Tietze()) - Algebra = FreeAlgebra(R, m*3, [f'{s}p_{i}' - for i in range(m) for s in 'bca' - if self.Tietze()[i] > 0] + [f'{s}m_{i}' - for i in range(m) for s in 'bca' - if self.Tietze()[i] < 0]) - gen_indizes = [i for i in range(m) if self.Tietze()[i] > 0] + \ - [i for i in range(m) if self.Tietze()[i] < 0] - - M = identity_matrix(Algebra, n) + alg = FreeAlgebra(R, m*3, [f'{s}p_{i}' + for i in range(m) if self.Tietze()[i] > 0 + for s in 'bca'] + + [f'{s}m_{i}' + for i in range(m) if self.Tietze()[i] < 0 + for s in 'bca']) + gen_indices = ([i for i in range(m) if self.Tietze()[i] > 0] + + [i for i in range(m) if self.Tietze()[i] < 0]) + + M = identity_matrix(alg, n) for k, i in enumerate(self.Tietze()): - A = identity_matrix(Algebra, n) - gen_index = gen_indizes.index(k) - b, c, a = Algebra.gens()[3*gen_index:3*gen_index+3] + A = identity_matrix(alg, n) + gen_index = gen_indices.index(k) + b, c, a = alg.gens()[3*gen_index:3*gen_index+3] if i > 0: A[i-1, i-1] = a A[i, i] = 0 @@ -2037,14 +2038,14 @@ def deformed_burau_matrix(self, variab='q'): M = M * A return M - @cached_method def _colored_jones_sum(self, N, qword): - r"""Helper function to get the colored Jones polynomial. + r""" + Helper function to get the colored Jones polynomial. INPUT: - - ``N`` -- An integer; the number of colors. - - ``qword`` -- A right quantum word (possibly in unreduced form). + - ``N`` -- integer; the number of colors + - ``qword`` -- a right quantum word (possibly in unreduced form) EXAMPLES:: @@ -2061,7 +2062,7 @@ def _colored_jones_sum(self, N, qword): rqword = RightQuantumWord(qword).reduced_word() alg = qword.parent() R = alg.base_ring() - result = R(1) + result = R.one() current_word = alg(1) i = 1 continue_summing = True @@ -2074,7 +2075,7 @@ def _colored_jones_sum(self, N, qword): current_word = new_rqw.reduced_word() new_eps = new_rqw.eps(N) result += new_eps - if not (new_eps): + if not new_eps: continue_summing = False i += 1 return result @@ -2085,11 +2086,17 @@ def colored_jones_polynomial(self, N, variab=None, try_inverse=True): INPUT: - - ``N`` -- integer; the number of colors. - - ``variab`` -- string (default: ``q``); the variable in the - resulting laurent polynomial. + - ``N`` -- integer; the number of colors + - ``variab`` -- (default: `q`) the variable in the resulting + Laurent polynomial - ``try_inverse`` -- boolean (default: ``True``); if ``True``, - attempt a faster calculation by using the inverse of the braid. + attempt a faster calculation by using the inverse of the braid + + ALGORITHM: + + The algorithm used is described in [HL2018]_. We follow their + notation, but work in a suitable free algebra over a Laurent + polynomial ring in one variable to simplify bookkeeping. EXAMPLES:: @@ -2097,70 +2104,85 @@ def colored_jones_polynomial(self, N, variab=None, try_inverse=True): sage: trefoil.colored_jones_polynomial(2) q + q^3 - q^4 sage: trefoil.colored_jones_polynomial(4) - q^3 + q^7 - q^10 + q^11 - q^13 - q^14 + q^15 - q^17 + q^19 + q^20 - - q^21 + q^3 + q^7 - q^10 + q^11 - q^13 - q^14 + q^15 - q^17 + + q^19 + q^20 - q^21 sage: trefoil.inverse().colored_jones_polynomial(4) - -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - - q^-10 + q^-7 + q^-3 + -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + + q^-11 - q^-10 + q^-7 + q^-3 sage: figure_eight = BraidGroup(3)([-1, 2, -1, 2]) sage: figure_eight.colored_jones_polynomial(2) q^-2 - q^-1 + 1 - q + q^2 sage: figure_eight.colored_jones_polynomial(3, 'Q') - Q^-6 - Q^-5 - Q^-4 + 2*Q^-3 - Q^-2 - Q^-1 + 3 - Q - Q^2 + 2*Q^3 - - Q^4 - Q^5 + Q^6 - - ALGORITHM: - - The algorithm used is described in [HL2018]_. We follow their notation, - but work in a suitable free algebra over a Laurent polynomial ring in - one variable to simplify bookkeeping. + Q^-6 - Q^-5 - Q^-4 + 2*Q^-3 - Q^-2 - Q^-1 + 3 - Q - Q^2 + + 2*Q^3 - Q^4 - Q^5 + Q^6 """ if self.components_in_closure() != 1: raise ValueError("the number of components must be 1") + if not hasattr(self, '_cj_with_q'): + # Move to the __init__ if this class adds one + self._cj_with_q = {} + if N in self._cj_with_q: + cj = self._cj_with_q[N] + if variab is None: + return cj + if isinstance(variab, str): + variab = LaurentPolynomialRing(IntegerRing(), variab).gen() + return cj.subs(q=variab) + db = self.deformed_burau_matrix('q')[1:, 1:] q = db.parent().base_ring().base_ring().gen() n = db.ncols() - qword = sum((-1)**(s.cardinality() - 1)*(q*db[list(s), - list(s)]).quantum_determinant(q) + qword = sum((-1)**(s.cardinality() - 1) + * (q * db[list(s), list(s)]).quantum_determinant(q) for s in Subsets(range(n)) if s) inverse_shorter = try_inverse if try_inverse: db_inv = self.inverse().deformed_burau_matrix('q')[1:, 1:] q_inv = db_inv.parent().base_ring().base_ring().gen() - qword_inv = sum((-1)**(s.cardinality() - - 1)*(q_inv*db_inv[list(s), - list(s)]).quantum_determinant(q) + qword_inv = sum((-1)**(s.cardinality() - 1) + * (q_inv*db_inv[list(s), list(s)]).quantum_determinant(q) for s in Subsets(range(n)) if s) # Check if the inverse has a shorter expression at this point inverse_shorter = len(list(qword_inv)) < len(list(qword)) use_inverse = try_inverse and inverse_shorter shorter_qword = qword_inv if use_inverse else qword knot = Knot(self.inverse()) if use_inverse else Knot(self) - cj = q**(((N - 1)*(knot.writhe() - self.strands() + 1))/2) * \ - self._colored_jones_sum(N, shorter_qword).leading_coefficient() - cj_with_q = cj.subs({q: 1/q}) if use_inverse else cj - - # Up to this point, we have calculated everyting with a variable named - # `q` instead of using `variab`, because this allows proper caching in - # `_colored_jones_sum`. Here we do the substitution as necessary. - if not variab: - return cj_with_q - new_q = LaurentPolynomialRing(IntegerRing(), variab).gen() - return cj_with_q.subs({q: new_q}) + cj = (q**((N - 1) * (knot.writhe() - self.strands() + 1) / 2) + * self._colored_jones_sum(N, shorter_qword).leading_coefficient()) + self._cj_with_q[N] = cj.subs({q: 1/q}) if use_inverse else cj + return self.colored_jones_polynomial(N, variab, try_inverse) class RightQuantumWord: - def __init__(self, words): - r""" - An internal class representing right quantum words as in - definition 4.1 of [HL2018]_. + """ + A right quantum word as in Definition 4.1 of [HL2018]_. - INPUT: + INPUT: - - ``words`` -- An element in a suitable free algebra over a Laurent - polynomial ring in one variable. This input does not need to be in - reduced form, but the monomials for the input can come in any order. + - ``words`` -- an element in a suitable free algebra over a Laurent + polynomial ring in one variable; this input does not need to be in + reduced form, but the monomials for the input can come in any order + + EXAMPLES:: + + sage: from sage.groups.braid import RightQuantumWord + sage: fig_8 = BraidGroup(3)([-1, 2, -1, 2]) + sage: ( + ....: bp_1, cp_1, ap_1, + ....: bp_3, cp_3, ap_3, + ....: bm_0, cm_0, am_0, + ....: bm_2, cm_2, am_2 + ....: ) = fig_8.deformed_burau_matrix().parent().base_ring().gens() + sage: q = bp_1.base_ring().gen() + sage: RightQuantumWord(ap_1*cp_1 + q**3*bm_2*bp_1*am_0*cm_0) + The right quantum word represented by + q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 + reduced from ap_1*cp_1 + q^3*bm_2*bp_1*am_0*cm_0 + """ + def __init__(self, words): + r""" + Initialize ``self``. EXAMPLES:: @@ -2173,28 +2195,26 @@ def __init__(self, words): ....: bm_2, cm_2, am_2 ....: ) = fig_8.deformed_burau_matrix().parent().base_ring().gens() sage: q = bp_1.base_ring().gen() - sage: RightQuantumWord(ap_1*cp_1 + q**3*bm_2*bp_1*am_0*cm_0) - The right quantum word represented by - q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 - reduced from ap_1*cp_1 + q^3*bm_2*bp_1*am_0*cm_0 - """ - self._Alg = words.parent() - self.q = self._Alg.base_ring().gen() - self.R = self._Alg.base_ring() + sage: Q = RightQuantumWord(ap_1*cp_1 + q**3*bm_2*bp_1*am_0*cm_0) + sage: TestSuite(Q).run(skip="_test_pickling") + """ + self._algebra = words.parent() + self.q = self._algebra.base_ring().gen() + self.R = self._algebra.base_ring() self._unreduced_words = words - self._tuples = None - self._gens = self._Alg.gens() - self._minus_begin = min((i for i, gen in enumerate(self._gens) if - 'm' in str(gen)), default=len(self._gens)) + self._gens = self._algebra.gens() + self._minus_begin = min((i for i, gen in enumerate(self._gens) if 'm' in str(gen)), + default=len(self._gens)) - def as_tuples(self): + @lazy_attribute + def tuples(self): r""" - Get a representation of the right quantum word as a dict, with + Get a representation of the right quantum word as a ``dict``, with keys monomials in the free algebra represented as tuples and values in elements the Laurent polynomial ring in one variable. - This is in the reduced form as outlines in definition of 4.1 of - [HL2018]_. + This is in the reduced form as outlined in Definition 4.1 + of [HL2018]_. OUTPUT: @@ -2214,39 +2234,34 @@ def as_tuples(self): sage: q = bp_1.base_ring().gen() sage: qw = RightQuantumWord(ap_1*cp_1 + ....: q**3*bm_2*bp_1*am_0*cm_0) - sage: for key, value in qw.as_tuples().items(): + sage: for key, value in qw.tuples.items(): ....: print(key, value) ....: (0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0) q (1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0) q^2 """ - if self._tuples: - return self._tuples - self._tuples = collections.defaultdict(self.R) + from collections import defaultdict + ret = defaultdict(self.R) for unreduced_monom, q_power in list(self._unreduced_words): q = self.q ret_tuple = [0] * len(self._gens) for gen, exp in unreduced_monom: gen_index = self._gens.index(gen) - is_minus = gen_index >= self._minus_begin - is_a = not(bool((gen_index+1) % 3)) - is_b = not(bool(gen_index % 3)) - is_c = not(bool((gen_index+2) % 3)) - index = gen_index//3 + is_minus = bool(gen_index >= self._minus_begin) + index = gen_index // 3 # This uses the relations in equations (4.1) and (4.2) # of [HL2018]_. i, j, k = ret_tuple[3*index: 3*index + 3] - if is_a: + if not (gen_index + 1) % 3: # is_a ret_tuple[3*index: 3*index + 3] = [i, j, k + exp] - if is_b: + if not gen_index % 3: # is_b ret_tuple[3*index: 3*index + 3] = [i + exp, j, k] - q_power *= q**(2*(k*exp + j*exp)) if is_minus \ - else q**(-2*j*exp) - if is_c: + q_power *= q**(2*(k*exp + j*exp)) if is_minus else q**(-2*j*exp) + if not (gen_index + 2) % 3: # is_c ret_tuple[3*index: 3*index + 3] = [i, j + exp, k] q_power *= q**(-k*exp) if is_minus else q**(k*exp) - self._tuples[tuple(ret_tuple)] += self._Alg(q_power) - return self._tuples + ret[tuple(ret_tuple)] += self._algebra(q_power) + return ret def reduced_word(self): r""" @@ -2254,7 +2269,7 @@ def reduced_word(self): OUTPUT: - An element in the free algebra self._Alg. + An element in the free algebra. EXAMPLES:: @@ -2272,9 +2287,9 @@ def reduced_word(self): sage: qw.reduced_word() q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 - TESTS:: + TESTS: - Testing the equations (4.1) and (4.2) in [HL2018]_. + Testing the equations (4.1) and (4.2) in [HL2018]_:: sage: RightQuantumWord(ap_3*bp_3).reduced_word() bp_3*ap_3 @@ -2290,21 +2305,23 @@ def reduced_word(self): q^2*bm_2*cm_2 .. TODO:: + Paralellize this function, calculating all summands in the sum in parallel. """ def tuple_to_word(q_tuple): - return prod(self._gens[i]**exp + return prod(self._gens[i] ** exp for i, exp in enumerate(q_tuple)) - return sum(q_factor*tuple_to_word(q_tuple) - for q_tuple, q_factor in self.as_tuples().items()) + return sum(q_factor * tuple_to_word(q_tuple) + for q_tuple, q_factor in self.tuples.items()) def eps(self, N): - r"""Evaluate the map $\mathcal{E}_N$ for a braid. + r""" + Evaluate the map `\mathcal{E}_N` for a braid. INPUT: - - ``N`` -- an integer; the number of colors. + - ``N`` -- an integer; the number of colors EXAMPLES:: @@ -2342,7 +2359,7 @@ def eps_monom(q_tuple): for rj in range(self._minus_begin + 1, len(q_tuple), 3)) ret_q *= prod(prod(1 - q**(N - 1 - q_tuple[3*i + 1] - h) - for h in range(0, q_tuple[3*i + 2])) + for h in range(q_tuple[3*i + 2])) for i in range(self._minus_begin//3)) ret_q *= prod(prod(1 - q**(q_tuple[3*j + 1] + l + 1 - N) for l in range(q_tuple[3*j + 2])) @@ -2350,12 +2367,12 @@ def eps_monom(q_tuple): len(q_tuple)//3)) return ret_q - return sum(q_factor*eps_monom(q_tuple) - for q_tuple, q_factor in self.as_tuples().items()) + return sum(q_factor * eps_monom(q_tuple) + for q_tuple, q_factor in self.tuples.items()) def __repr__(self): r""" - String representation of the reight quantum word. + String representation of ``self``. EXAMPLES:: @@ -2367,9 +2384,9 @@ def __repr__(self): The right quantum word represented by cp_1*bp_3*am_2 reduced from cp_1*am_2*bp_3 """ - return 'The right quantum word represented by ' + \ - f'{str(self.reduced_word())} reduced from ' + \ - f'{str(self._unreduced_words)}' + return ('The right quantum word represented by ' + + f'{str(self.reduced_word())} reduced from ' + + f'{str(self._unreduced_words)}') class BraidGroup_class(FiniteTypeArtinGroup): From bac98e5dfbd758c3f67111be6ee1a6843c40c704 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 8 Oct 2021 10:54:12 +0900 Subject: [PATCH 108/122] Adding colored Jones polynomial to knots. --- src/sage/knots/knot.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/sage/knots/knot.py b/src/sage/knots/knot.py index 358868c2565..10f0e241608 100644 --- a/src/sage/knots/knot.py +++ b/src/sage/knots/knot.py @@ -74,6 +74,8 @@ class Knot(Link, Element, metaclass=InheritComparisonClasscallMetaclass): 31 sage: K.signature() -2 + sage: K.colored_jones_polynomial(2) # long time + q^-1 - 2 + 4*q - 5*q^2 + 6*q^3 - 5*q^4 + 4*q^5 - 3*q^6 + q^7 REFERENCES: @@ -349,6 +351,44 @@ def arf_invariant(self): return 0 return 1 + def colored_jones_polynomial(self, N, variab=None, try_inverse=True): + r""" + Return the colored Jones polynomial of the trace closure of the braid. + + INPUT: + + - ``N`` -- integer; the number of colors + - ``variab`` -- (default: `q`) the variable in the resulting + Laurent polynomial + - ``try_inverse`` -- boolean (default: ``True``); if ``True``, + attempt a faster calculation by using the inverse of the braid + + ALGORITHM: + + The algorithm used is described in [HL2018]_ for the corresponding + braid representation. We follow their notation, but work in a + suitable free algebra over a Laurent polynomial ring in one + variable to simplify bookkeeping. + + EXAMPLES:: + + sage: W = Knots() + sage: K = W.from_dowker_code([-4,-6,-2]) + sage: K.colored_jones_polynomial(2) + -q^-4 + q^-3 + q^-1 + sage: K.colored_jones_polynomial(2, 't') + -t^-4 + t^-3 + t^-1 + sage: R. = LaurentPolynomialRing(ZZ) + sage: K.colored_jones_polynomial(2, -t) + -t^-4 - t^-3 - t^-1 + + sage: R. = ZZ[] + sage: K.colored_jones_polynomial(2, t+1) + (t^3 + 3*t^2 + 4*t + 1)/(t^4 + 4*t^3 + 6*t^2 + 4*t + 1) + """ + return self.braid().colored_jones_polynomial(N=N, variab=variab, + try_inverse=try_inverse) + def connected_sum(self, other): r""" Return the oriented connected sum of ``self`` and ``other``. From 93cc25eefa6587c24d05d88f8dab86c83305efce Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 8 Oct 2021 11:16:13 +0900 Subject: [PATCH 109/122] Speedup of RightQuantumWord.tuples computation. --- src/sage/groups/braid.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index fe55c7d235a..89b116f1500 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -72,7 +72,6 @@ from sage.misc.lazy_import import lazy_import from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod -from sage.algebras.free_algebra import FreeAlgebra from sage.categories.groups import Groups from sage.groups.free_group import FreeGroup, is_FreeGroup from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing @@ -2011,6 +2010,7 @@ def deformed_burau_matrix(self, variab='q'): R = LaurentPolynomialRing(IntegerRing(), variab) n = self.strands() m = len(self.Tietze()) + from sage.algebras.free_algebra import FreeAlgebra alg = FreeAlgebra(R, m*3, [f'{s}p_{i}' for i in range(m) if self.Tietze()[i] > 0 for s in 'bca'] @@ -2203,6 +2203,7 @@ def __init__(self, words): self.R = self._algebra.base_ring() self._unreduced_words = words self._gens = self._algebra.gens() + self._gens_index = {g: i for i, g in enumerate(self._algebra._indices.gens())} self._minus_begin = min((i for i, gen in enumerate(self._gens) if 'm' in str(gen)), default=len(self._gens)) @@ -2219,7 +2220,7 @@ def tuples(self): OUTPUT: A dict of tuples of ints corresponding to the exponents in the - generators self._gens(), with values in self.R. + generators with values in the algebra's base ring. EXAMPLES:: @@ -2246,7 +2247,7 @@ def tuples(self): q = self.q ret_tuple = [0] * len(self._gens) for gen, exp in unreduced_monom: - gen_index = self._gens.index(gen) + gen_index = self._gens_index[gen] is_minus = bool(gen_index >= self._minus_begin) index = gen_index // 3 # This uses the relations in equations (4.1) and (4.2) From 2121743b84b822c443a4e1d45081d39244191aa6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 8 Oct 2021 11:33:51 +0900 Subject: [PATCH 110/122] Further speedup to colored_jones_polynomial(). --- src/sage/groups/braid.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 89b116f1500..ab9d1ad92ba 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2053,17 +2053,17 @@ def _colored_jones_sum(self, N, qword): sage: db = b.deformed_burau_matrix()[1:,1:]; db [cp_0*ap_1*bp_2] sage: b._colored_jones_sum(2, db[0,0]) - (1+q-q^2) + 1 + q - q^2 sage: b._colored_jones_sum(3, db[0,0]) - (1+q^2-q^5-q^6+q^7) + 1 + q^2 - q^5 - q^6 + q^7 sage: b._colored_jones_sum(4, db[0,0]) - (1+q^3-q^8-q^10+q^13+q^14-q^15) + 1 + q^3 - q^8 - q^10 + q^13 + q^14 - q^15 """ rqword = RightQuantumWord(qword).reduced_word() alg = qword.parent() R = alg.base_ring() result = R.one() - current_word = alg(1) + current_word = alg.one() i = 1 continue_summing = True # This seemingly infinite sum is always finite if the qword comes @@ -2149,7 +2149,7 @@ def colored_jones_polynomial(self, N, variab=None, try_inverse=True): shorter_qword = qword_inv if use_inverse else qword knot = Knot(self.inverse()) if use_inverse else Knot(self) cj = (q**((N - 1) * (knot.writhe() - self.strands() + 1) / 2) - * self._colored_jones_sum(N, shorter_qword).leading_coefficient()) + * self._colored_jones_sum(N, shorter_qword)) self._cj_with_q[N] = cj.subs({q: 1/q}) if use_inverse else cj return self.colored_jones_polynomial(N, variab, try_inverse) @@ -2202,8 +2202,8 @@ def __init__(self, words): self.q = self._algebra.base_ring().gen() self.R = self._algebra.base_ring() self._unreduced_words = words - self._gens = self._algebra.gens() - self._gens_index = {g: i for i, g in enumerate(self._algebra._indices.gens())} + self._gens = self._algebra._indices.gens() + self._gens_index = {g: i for i, g in enumerate(self._gens)} self._minus_begin = min((i for i, gen in enumerate(self._gens) if 'm' in str(gen)), default=len(self._gens)) @@ -2261,7 +2261,7 @@ def tuples(self): if not (gen_index + 2) % 3: # is_c ret_tuple[3*index: 3*index + 3] = [i, j + exp, k] q_power *= q**(-k*exp) if is_minus else q**(k*exp) - ret[tuple(ret_tuple)] += self._algebra(q_power) + ret[tuple(ret_tuple)] += q_power return ret def reduced_word(self): @@ -2310,11 +2310,13 @@ def reduced_word(self): Paralellize this function, calculating all summands in the sum in parallel. """ + M = self._algebra._indices def tuple_to_word(q_tuple): - return prod(self._gens[i] ** exp - for i, exp in enumerate(q_tuple)) - return sum(q_factor * tuple_to_word(q_tuple) - for q_tuple, q_factor in self.tuples.items()) + return M.prod(self._gens[i] ** exp + for i, exp in enumerate(q_tuple)) + ret = {tuple_to_word(q_tuple): q_factor + for q_tuple, q_factor in self.tuples.items() if q_factor} + return self._algebra._from_dict(ret, remove_zeros=False) def eps(self, N): r""" @@ -2338,9 +2340,9 @@ def eps(self, N): sage: rqw = RightQuantumWord( ....: q^3*bp_2*bp_0*ap_0 + q*ap_3*bm_1*am_1*bp_0) sage: rqw.eps(3) - -(q^-1-2*q+q^5) + -q^-1 + 2*q - q^5 sage: rqw.eps(2) - -(1-2*q+q^2-q^3+q^4) + -1 + 2*q - q^2 + q^3 - q^4 TESTS:: @@ -2348,6 +2350,7 @@ def eps(self, N): 0 .. TODO:: + Paralellize this function, calculating all summands in the sum in parallel. """ From 524906e03f6dec6be462b7210289c2bf2a5ffcf9 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Thu, 21 Oct 2021 16:27:59 +0200 Subject: [PATCH 111/122] fix q_inv typo --- src/sage/groups/braid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index ab9d1ad92ba..b61f8f330a7 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2141,7 +2141,7 @@ def colored_jones_polynomial(self, N, variab=None, try_inverse=True): db_inv = self.inverse().deformed_burau_matrix('q')[1:, 1:] q_inv = db_inv.parent().base_ring().base_ring().gen() qword_inv = sum((-1)**(s.cardinality() - 1) - * (q_inv*db_inv[list(s), list(s)]).quantum_determinant(q) + * (q_inv*db_inv[list(s), list(s)]).quantum_determinant(q_inv) for s in Subsets(range(n)) if s) # Check if the inverse has a shorter expression at this point inverse_shorter = len(list(qword_inv)) < len(list(qword)) From d2722d0b1128b578fc6e88d144f8a8af21cc9805 Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Thu, 21 Oct 2021 17:07:48 +0200 Subject: [PATCH 112/122] fix botched rebase --- src/sage/groups/braid.py | 214 --------------------------------------- 1 file changed, 214 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index b61f8f330a7..a6e53f2e1a1 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -1702,7 +1702,6 @@ def sliding_circuits(self): B = self.parent() return [[B._element_from_libbraiding(i) for i in s] for s in slc] -<<<<<<< HEAD def mirror_image(self): r""" Return the image of ``self`` under the mirror involution (see @@ -1742,220 +1741,7 @@ def reverse(self): t.reverse() return self.parent()(tuple(t)) - class _RightQuantumWord: - def __init__(self, words): - r""" - An internal class representing right quantum words as in - definition 4.1 of [HL2018]_. - - INPUT: - - ``words`` -- An element in a suitable free algebra over a - Laurent polynomial ring in one variable. This input does not - need to be in reduced form, but the monomials for the input - can come in any order. - - EXAMPLES:: - - sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) - sage: ( - ....: bp_1, cp_1, ap_1, - ....: bp_3, cp_3, ap_3, - ....: bm_0, cm_0, am_0, - ....: bm_2, cm_2, am_2 - ....: ) = figure_8._deformed_burau_matrix().parent().base_ring().gens() - sage: q = bp_1.base_ring().gen() - sage: figure_8._RightQuantumWord(ap_1*cp_1 + - ....: q**3*bm_2*bp_1*am_0*cm_0) - The right quantum word represented by q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 reduced from ap_1*cp_1 + q^3*bm_2*bp_1*am_0*cm_0 - """ - self._Alg = words.parent() - self.q = self._Alg.base_ring().gen() - self.R = self._Alg.base_ring() - self._unreduced_words = words - self._tuples = None - self._gens = self._Alg.gens() - self._minus_begin = min((i for i, gen in enumerate(self._gens) if - 'm' in str(gen)), default=len(self._gens)) - - def as_tuples(self): - r""" - Get a representation of the right quantum word as a dict, with - keys monomials in the free algebra represented as tuples and - values in elements the Laurent polynomial ring in one variable. - - This is in the reduced form as outlines in definition of 4.1 of - [HL2018]_. - - OUTPUT: - A dict of tuples of ints corresponding to the exponents in the - generators self._gens(), with values in self.R. - - EXAMPLES:: - - sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) - sage: ( - ....: bp_1, cp_1, ap_1, - ....: bp_3, cp_3, ap_3, - ....: bm_0, cm_0, am_0, - ....: bm_2, cm_2, am_2 - ....: ) = figure_8._deformed_burau_matrix().parent().base_ring().gens() - sage: q = bp_1.base_ring().gen() - sage: qw = figure_8._RightQuantumWord(ap_1*cp_1 + - ....: q**3*bm_2*bp_1*am_0*cm_0) - sage: for key, value in qw.as_tuples().items(): - ....: print(key, value) - ....: - (0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0) q - (1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0) q^2 - """ - if self._tuples: - return self._tuples - self._tuples = collections.defaultdict(self.R) - for unreduced_monom, q_power in list(self._unreduced_words): - q = self.q - ret_tuple = [0] * len(self._gens) - for gen, exp in unreduced_monom: - gen_index = self._gens.index(gen) - is_minus = gen_index >= self._minus_begin - is_a = not(bool((gen_index+1) % 3)) - is_b = not(bool(gen_index % 3)) - is_c = not(bool((gen_index+2) % 3)) - index = gen_index//3 - # This uses the relations in equations (4.1) and (4.2) - # of [HL2018]_. - i, j, k = ret_tuple[3*index: 3*index + 3] - if is_a: - ret_tuple[3*index: 3*index + 3] = [i, j, k + exp] - if is_b: - ret_tuple[3*index: 3*index + 3] = [i + exp, j, k] - q_power *= q**(2*(k*exp + j*exp)) if is_minus \ - else q**(-2*j*exp) - if is_c: - ret_tuple[3*index: 3*index + 3] = [i, j + exp, k] - q_power *= q**(-k*exp) if is_minus else q**(k*exp) - self._tuples[tuple(ret_tuple)] += self._Alg(q_power) - return self._tuples - - def reduced_word(self): - r""" - Return the (reduced) right quantum word. - - OUTPUT: - An element in the free algebra self._Alg. - - EXAMPLES:: - - sage: figure_8 = BraidGroup(3)([-1, 2, -1, 2]) - sage: ( - ....: bp_1, cp_1, ap_1, - ....: bp_3, cp_3, ap_3, - ....: bm_0, cm_0, am_0, - ....: bm_2, cm_2, am_2 - ....: ) = figure_8._deformed_burau_matrix().parent().base_ring().gens() - sage: q = bp_1.base_ring().gen() - sage: qw = figure_8._RightQuantumWord(ap_1*cp_1 + - ....: q**3*bm_2*bp_1*am_0*cm_0) - sage: qw.reduced_word() - q*cp_1*ap_1 + q^2*bp_1*cm_0*am_0*bm_2 - - TESTS:: - - Testing the equations (4.1) and (4.2) in [HL2018]_. - - sage: figure_8._RightQuantumWord(ap_3*bp_3).reduced_word() - bp_3*ap_3 - sage: figure_8._RightQuantumWord(ap_3*cp_3).reduced_word() - q*cp_3*ap_3 - sage: figure_8._RightQuantumWord(cp_3*bp_3).reduced_word() - (q^-2)*bp_3*cp_3 - sage: figure_8._RightQuantumWord(am_2*bm_2).reduced_word() - q^2*bm_2*am_2 - sage: figure_8._RightQuantumWord(am_2*cm_2).reduced_word() - (q^-1)*cm_2*am_2 - sage: figure_8._RightQuantumWord(cm_2*bm_2).reduced_word() - q^2*bm_2*cm_2 - - .. TODO:: - Paralellize this function, calculating all summands in the sum - in parallel. - """ - def tuple_to_word(q_tuple): - return prod(self._gens[i]**exp - for i, exp in enumerate(q_tuple)) - return sum(q_factor*tuple_to_word(q_tuple) - for q_tuple, q_factor in self.as_tuples().items()) - - def eps(self, N): - r"""Evaluate the map $\mathcal{E}_N$ for a braid. - - INPUT: - - ``N`` -- an integer; the number of colors. - - EXAMPLES:: - - sage: B = BraidGroup(3) - sage: b = B([1,-2,1,2]) - sage: db = b._deformed_burau_matrix()[:, :] - sage: q = db.parent().base_ring().base_ring().gen() - sage: (bp_0, cp_0, ap_0, - ....: bp_2, cp_2, ap_2, - ....: bp_3, cp_3, ap_3, - ....: bm_1, cm_1, am_1) = db.parent().base_ring().gens() - sage: rqw = b._RightQuantumWord( - ....: q^3*bp_2*bp_0*ap_0 + q*ap_3*bm_1*am_1*bp_0) - sage: rqw.eps(3) - -(q^-1-2*q+q^5) - sage: rqw.eps(2) - -(1-2*q+q^2-q^3+q^4) - - TESTS:: - - sage: rqw.eps(1) - 0 - - .. TODO:: - Paralellize this function, calculating all summands in the sum - in parallel. - """ - def eps_monom(q_tuple): - q = self.q - r"""Evaluate the map $\mathcal{E}_N$ for a single mononial.""" - ret_q = q**sum((N - 1 - q_tuple[3*i + 2])*q_tuple[3*i + 1] - for i in range(self._minus_begin//3)) - ret_q *= q**sum((N - 1)*(-q_tuple[rj]) - for rj in range(self._minus_begin + 1, - len(q_tuple), 3)) - ret_q *= prod(prod(1 - q**(N - 1 - q_tuple[3*i + 1] - h) - for h in range(0, q_tuple[3*i + 2])) - for i in range(self._minus_begin//3)) - ret_q *= prod(prod(1 - q**(q_tuple[3*j + 1] + l + 1 - N) - for l in range(q_tuple[3*j + 2])) - for j in range(self._minus_begin//3, - len(q_tuple)//3)) - return ret_q - - return sum(q_factor*eps_monom(q_tuple) - for q_tuple, q_factor in self.as_tuples().items()) - - def __repr__(self): - r""" - String representation of the reight quantum word. - - EXAMPLES:: - sage: b = BraidGroup(3)([1,2,-1,2,-1]) - sage: db = b._deformed_burau_matrix(); db[2,2] - cp_1*am_2*bp_3 - sage: b._RightQuantumWord(db[2,2]) - The right quantum word represented by cp_1*bp_3*am_2 reduced from cp_1*am_2*bp_3 - """ - return 'The right quantum word represented by ' + \ - f'{str(self.reduced_word())} reduced from ' + \ - f'{str(self._unreduced_words)}' - - def _deformed_burau_matrix(self, variab='q'): -======= def deformed_burau_matrix(self, variab='q'): ->>>>>>> 062a9e9a44 (first round of review comments) r""" Return the deformed Burau matrix of the braid. From f76ae2c8207453e8ace5214861960c3f0500d1fd Mon Sep 17 00:00:00 2001 From: Moritz Firsching Date: Fri, 22 Oct 2021 09:18:30 +0200 Subject: [PATCH 113/122] chnage order of indices in quantum det doc test --- src/sage/matrix/matrix2.pyx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 75d8dc56424..6d8c3fd33e4 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -2136,15 +2136,13 @@ cdef class Matrix(Matrix1): EXAMPLES:: - sage: A = matrix([[SR(f'a{i}{j}') for i in range(2)] - ....: for j in range(2)]); A - [a00 a10] - [a01 a11] + sage: A = matrix(SR, 2, lambda i, j: f'a{i}{j}'); A + [a00 a01] + [a10 a11] sage: A.quantum_determinant() -a01*a10*q + a00*a11 - sage: A = matrix([[SR(f'a{i}{j}') for i in range(3)] - ....: for j in range(3)]) + sage: A = matrix(SR, 3, lambda i, j: f'a{i}{j}') sage: A.quantum_determinant() -a02*a11*a20*q^3 + (a01*a12*a20 + a02*a10*a21)*q^2 + (-a00*a12*a21 - a01*a10*a22)*q + a00*a11*a22 From e21c9bc0be543344e721fff2c1227a138456505d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 22 Oct 2021 15:01:49 +0200 Subject: [PATCH 114/122] remove non-existing links --- src/doc/it/tutorial/tour_algebra.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/doc/it/tutorial/tour_algebra.rst b/src/doc/it/tutorial/tour_algebra.rst index e17a93dde8a..20ee65727fa 100644 --- a/src/doc/it/tutorial/tour_algebra.rst +++ b/src/doc/it/tutorial/tour_algebra.rst @@ -251,8 +251,6 @@ Le singole componenti possono essere tracciate usando:: sage: p2 = plot(4*cos(t) - cos(2*t), 0, 2*pi, rgbcolor=hue(0.6)) sage: show(p1 + p2) -(Per ulteriori informazioni sul disegno di funzioni, si veda :ref:`section-plot`.) - BIBLIOGRAFIA: Nagle, Saff, Snider, Fundamentals of Differential Equations, 6th ed, Addison-Wesley, 2004. (si veda § 5.5). @@ -357,8 +355,6 @@ come mostrato qui in seguito: sage: show(P[0] + P[1]) -(Per ulteriori informazioni sul disegno di grafici, si veda :ref:`section-plot`.) - Funzioni speciali ----------------- From 364305235fb2b80536fc2dcb21185c84e8396250 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 23 Oct 2021 18:43:10 -0700 Subject: [PATCH 115/122] src/sage/rings/abc.pyx: Restore lost RealField --- src/sage/rings/abc.pyx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/rings/abc.pyx b/src/sage/rings/abc.pyx index 10073c6bcf9..e9beb3310ff 100644 --- a/src/sage/rings/abc.pyx +++ b/src/sage/rings/abc.pyx @@ -35,6 +35,14 @@ class AlgebraicField(AlgebraicField_common): class AlgebraicRealField(AlgebraicField_common): + r""" + Abstract base class for :class:`~sage.rings.qqbar.AlgebraicRealField`. + """ + + pass + + +cdef class RealField(Field): r""" Abstract base class for :class:`~sage.rings.real_mpfr.RealField_class`. """ From 57e8e9bc41c8e41eca5cad8879e67ba49089a4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 Oct 2021 17:00:54 +0200 Subject: [PATCH 116/122] simple-minded check for string input of CC --- src/sage/interfaces/singular.py | 4 ++-- src/sage/rings/complex_mpfr.pyx | 18 +++++++++++++++++- .../schemes/elliptic_curves/period_lattice.py | 19 ++++++++++--------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 4c5cf3dfd6b..d7718b7aaf3 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1576,10 +1576,10 @@ def sage_global_ring(self): '' sage: R = singular('r5').sage_global_ring(); R Multivariate Polynomial Ring in a, b, c over Complex Field with 54 bits of precision - sage: R.base_ring()('j') + sage: R.base_ring()('k') Traceback (most recent call last): ... - NameError: name 'j' is not defined + ValueError: given string 'k' is not a complex number sage: R.base_ring()('I') 1.00000000000000*I diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index b419914f23f..3f072dea133 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -499,15 +499,31 @@ class ComplexField_class(sage.rings.abc.ComplexField): sage: CC(i) 1.00000000000000*I + TESTS:: + + sage: CC('1.2+3.4*j') + 1.20000000000000 + 3.40000000000000*I + sage: CC('hello') + Traceback (most recent call last): + ... + ValueError: given string 'hello' is not a complex number """ if not isinstance(x, (RealNumber, tuple)): if isinstance(x, ComplexDoubleElement): return ComplexNumber(self, x.real(), x.imag()) elif isinstance(x, str): + x = x.replace(' ', '') + x = x.replace('i', 'I') + x = x.replace('j', 'I') + x = x.replace('E', 'e') + allowed = '+-.*0123456789Ie' + if not all(letter in allowed for letter in x): + raise ValueError(f'given string {x!r} is not a complex number') + # This should rather use a proper parser to validate input. # TODO: this is probably not the best and most # efficient way to do this. -- Martin Albrecht return ComplexNumber(self, - sage_eval(x.replace(' ',''), locals={"I":self.gen(),"i":self.gen()})) + sage_eval(x, locals={"I": self.gen()})) late_import() if isinstance(x, NumberFieldElement_quadratic): diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 8ecd30c848c..812aee93b21 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -204,12 +204,12 @@ def __init__(self, E, embedding=None): K = E.base_field() if embedding is None: embs = K.embeddings(AA) - real = len(embs)>0 + real = len(embs) > 0 if not real: embs = K.embeddings(QQbar) embedding = embs[0] else: - embedding = refine_embedding(embedding,Infinity) + embedding = refine_embedding(embedding, Infinity) real = embedding(K.gen()).imag().is_zero() self.embedding = embedding @@ -1802,7 +1802,7 @@ def elliptic_exponential(self, z, to_curve=True): z = C(z) z_is_real = z.is_real() except TypeError: - raise TypeError("%s is not a complex number"%z) + raise TypeError("%s is not a complex number" % z) prec = C.precision() # test for the point at infinity: @@ -1813,7 +1813,7 @@ def elliptic_exponential(self, z, to_curve=True): if to_curve: return self.curve().change_ring(K)(0) else: - return (K('+infinity'), K('+infinity')) + return K(Infinity), K(Infinity) # general number field code (including QQ): @@ -1830,7 +1830,7 @@ def elliptic_exponential(self, z, to_curve=True): # the same precision as the input. x, y = pari(self.basis(prec=prec)).ellwp(z, flag=1) - x, y = [C(t) for t in (x,y)] + x, y = [C(t) for t in (x, y)] if self.real_flag and z_is_real: x = x.real() @@ -1839,14 +1839,15 @@ def elliptic_exponential(self, z, to_curve=True): if to_curve: K = x.parent() v = refine_embedding(self.embedding, Infinity) - a1,a2,a3,a4,a6 = [K(v(a)) for a in self.E.ainvs()] + a1, a2, a3, a4, a6 = [K(v(a)) for a in self.E.ainvs()] b2 = K(v(self.E.b2())) x = x - b2 / 12 y = (y - (a1 * x + a3)) / 2 - EK = EllipticCurve(K,[a1,a2,a3,a4,a6]) - return EK.point((x,y,K(1)), check=False) + EK = EllipticCurve(K, [a1, a2, a3, a4, a6]) + return EK.point((x, y, K.one()), check=False) else: - return (x,y) + return (x, y) + def reduce_tau(tau): r""" From 76d3bfd238e890d1fc8709c99ab9502b47f72078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Oct 2021 18:54:20 +0200 Subject: [PATCH 117/122] fix pycodestyle E713 and E714 in rings --- src/sage/rings/complex_interval_field.py | 8 +++---- src/sage/rings/derivation.py | 2 +- src/sage/rings/function_field/divisor.py | 2 +- .../rings/function_field/function_field.py | 6 ++--- src/sage/rings/function_field/order.py | 2 +- src/sage/rings/function_field/place.py | 4 ++-- src/sage/rings/invariants/reconstruction.py | 3 +-- .../polynomial/infinite_polynomial_ring.py | 4 ++-- .../polynomial/multi_polynomial_ideal.py | 2 +- .../polynomial_padic_capped_relative_dense.py | 22 +++++++++---------- .../padics/polynomial_padic_flat.py | 2 +- .../polynomial/polynomial_quotient_ring.py | 4 ++-- .../polynomial_quotient_ring_element.py | 2 +- src/sage/rings/polynomial/polynomial_ring.py | 2 +- .../polynomial/polynomial_ring_constructor.py | 6 ++--- src/sage/rings/power_series_ring.py | 3 +-- src/sage/rings/quotient_ring.py | 2 +- src/sage/rings/rational_field.py | 4 ++-- 18 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index 013977aa338..991398f4735 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -25,15 +25,15 @@ ``+/-1``. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein # # 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/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.structure.parent import Parent @@ -94,7 +94,7 @@ def ComplexIntervalField(prec=53, names=None): if prec in cache: X = cache[prec] C = X() - if not C is None: + if C is not None: return C C = ComplexIntervalField_class(prec) cache[prec] = weakref.ref(C) diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py index 39b27c5d16e..f719b711c45 100644 --- a/src/sage/rings/derivation.py +++ b/src/sage/rings/derivation.py @@ -247,7 +247,7 @@ def __init__(self, domain, codomain, twist=None): TypeError: the domain of the derivation must coerce to the domain of the twisting homomorphism """ - if not domain in Rings().Commutative(): + if domain not in Rings().Commutative(): raise TypeError("the domain must be a commutative ring") if codomain in Rings().Commutative() and codomain.has_coerce_map_from(domain): diff --git a/src/sage/rings/function_field/divisor.py b/src/sage/rings/function_field/divisor.py index 78a45efa2ea..7fd34197c1c 100644 --- a/src/sage/rings/function_field/divisor.py +++ b/src/sage/rings/function_field/divisor.py @@ -456,7 +456,7 @@ def multiplicity(self, place): sage: D.multiplicity(p2) -3 """ - if not place in self._data: + if place not in self._data: return 0 return self._data[place] diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 6c2945fbc64..9ab0c4de872 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -2893,7 +2893,7 @@ def places_above(self, p): """ R = self.base_field() - if not p in R.place_set(): + if p not in R.place_set(): raise TypeError("not a place of the base rational function field") if p.is_infinite_place(): @@ -3372,9 +3372,9 @@ def _weierstrass_places(self): gaps = [1] while M.nrows() < d: row = vector([der._derive(basis[i], e) for i in range(d)]) - if not row in M.row_space(): + if row not in M.row_space(): M = matrix(M.rows() + [row]) - gaps.append(e+1) + gaps.append(e + 1) e += 1 # This is faster than M.determinant(). Note that Mx diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index fd6a09ff9cb..ebe610bc504 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -1997,7 +1997,7 @@ def decomposition(self, ideal): row.append( V([to(e) for e in self._mtable[i][j]]) ) mtable.append(row) - if not p in self._kummer_places: + if p not in self._kummer_places: ##################################### # Decomposition by Kummer's theorem # ##################################### diff --git a/src/sage/rings/function_field/place.py b/src/sage/rings/function_field/place.py index 43abc67a6fb..f3400e76bdb 100644 --- a/src/sage/rings/function_field/place.py +++ b/src/sage/rings/function_field/place.py @@ -759,7 +759,7 @@ def _gaps_wronskian(self): gaps = [1] while M.nrows() < d: row = vector([to_R(der._derive(basis[i], e, sep)) for i in range(d)]) - if not row in M.row_space(): + if row not in M.row_space(): M = matrix(M.rows() + [row]) M.echelonize() gaps.append(e + 1) @@ -1065,7 +1065,7 @@ def from_K(e): alpha_powered_by_ramification_index = alpha ** prime._ramification_index def to_K(f): - if not f in O: + if f not in O: den = O.coordinate_vector(f).denominator() num = den * f diff --git a/src/sage/rings/invariants/reconstruction.py b/src/sage/rings/invariants/reconstruction.py index b9db4e07521..68126305ba4 100644 --- a/src/sage/rings/invariants/reconstruction.py +++ b/src/sage/rings/invariants/reconstruction.py @@ -383,8 +383,7 @@ def _reduce_invariants(invariants, weights): for prime in gcd(invariants).factor(): p = prime[0] for D in factors: - if not p in D: + if p not in D: D[p] = 0 scalar = scalar*p**min([factors[i][p]//weights[i] for i in range(n)]) return [invariants[i]*scalar**-weights[i] for i in range(n)] - diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index 3cf225c8cfe..84577be8e2b 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -709,11 +709,11 @@ def __init__(self, R, names, order): self._names = tuple(names) if not isinstance(order, str): raise TypeError("The monomial order must be given as a string") - if not R in Rings().Commutative(): + if R not in Rings().Commutative(): raise TypeError("The given 'base ring' (= %s) must be a commutative ring" % R) # now, the input is accepted - if hasattr(R,'_underlying_ring'): + if hasattr(R, '_underlying_ring'): self._underlying_ring = R._underlying_ring else: self._underlying_ring = R.base_ring() diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 560dcc8c25d..07e0656baa5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2238,7 +2238,7 @@ def quotient(self, J): if not isinstance(J, MPolynomialIdeal): raise TypeError("J needs to be a multivariate polynomial ideal") - if not R is J.ring() and not R == J.ring(): + if R is not J.ring() and not R == J.ring(): raise TypeError("base rings do not match") from sage.libs.singular.function_factory import ff diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 13d09467427..1fcfa993bca 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -87,7 +87,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, #We now coerce various types into lists of coefficients. There are fast pathways for some types of polynomials if isinstance(x, Polynomial): if x.parent() is self.parent(): - if not absprec is infinity or not relprec is infinity: + if absprec is not infinity or relprec is not infinity: x._normalize() self._poly = x._poly self._valbase = x._valbase @@ -95,7 +95,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, self._relprecs = x._relprecs self._normalized = x._normalized self._list = x._list - if not absprec is infinity or not relprec is infinity: + if absprec is not infinity or relprec is not infinity: self._adjust_prec_info(absprec, relprec) return elif x.base_ring() is ZZ: @@ -106,7 +106,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, self._comp_valaddeds() self._normalized = len(self._valaddeds) == 0 or (min(self._valaddeds) == 0) self._list = None - if not absprec is infinity or not relprec is infinity: + if absprec is not infinity or relprec is not infinity: self._adjust_prec_info(absprec, relprec) return else: @@ -147,14 +147,14 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, self._relprecs = [] self._poly = PolynomialRing(ZZ, parent.variable_name())() self._normalized = True - if not absprec is infinity or not relprec is infinity: + if absprec is not infinity or relprec is not infinity: self._adjust_prec_info(absprec, relprec) else: self._valaddeds = [c - self._valbase for c in self._valaddeds] self._relprecs = [a.precision_absolute() - self._valbase for a in x] self._poly = PolynomialRing(ZZ, parent.variable_name())([a >> self._valbase for a in x]) self._normalized = True - if not absprec is infinity or not relprec is infinity: + if absprec is not infinity or relprec is not infinity: self._adjust_prec_info(absprec, relprec) def _new_constant_poly(self, a, P): @@ -702,21 +702,21 @@ def _unsafe_mutate(self, n, value): self._poly._unsafe_mutate(self, n, 0) if n < len(self._relprecs): self._relprecs[n] = value.precision_absolute() - self._valbase - if not self._valaddeds is None: + if self._valaddeds is not None: self._valaddeds[n] = value.valuation() - self._valbase - if not self._list is None: + if self._list is not None: self._list[n] = value else: self._relprecs.extend([infinity] * (n - len(self._relprecs)) + [value.precision_absolute() - self._valbase]) - if not self._valaddeds is None: + if self._valaddeds is not None: self._valaddeds.extend([infinity] * (n - len(self._relprecs)) + [value.valuation() - self._valbase]) - if not self._list is None: + if self._list is not None: zero = self.base_ring()(0) self._list.extend([zero] * (n - len(self._relprecs)) + [value]) else: basediff = self._valbase - value.valuation() self._valbase = value.valuation() - if not self._valaddeds is None: + if self._valaddeds is not None: self._valaddeds = [c + basediff for c in self._valaddeds] self._poly = self._poly * self.base_ring().prime_pow(basediff) if value != 0: @@ -728,7 +728,7 @@ def _unsafe_mutate(self, n, value): else: self._relprecs.extend([infinity] * (n - len(self._relprecs)) + [value.precision_relative()]) self._normalized = False - if not self._list is None: + if self._list is not None: if n < len(self._list): self._list[n] = value else: diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py index 12e4104c56d..ee106a1e09a 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py @@ -57,7 +57,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False, ab else: m = sage.rings.padics.misc.min(a.precision_absolute() for a in x.values()) - if not absprec is None: + if absprec is not None: m = min(m, absprec) Polynomial_generic_dense.__init__(self, parent, x, absprec=m) return diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index b069cdd749c..f342447c8f0 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -1925,9 +1925,9 @@ def _isomorphic_ring(self): try: isomorphic_ring = self.base_ring().extension(self.modulus(), names=self.variable_names()) except ValueError: - pass # modulus is not irreducible + pass # modulus is not irreducible else: - if not isomorphic_ring in NumberFields(): + if isomorphic_ring not in NumberFields(): raise NotImplementedError("cannot handle extensions of number fields that do not produce number fields") # refine the category of self if not self.is_field(): diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 7c8679bedd9..65d10375c06 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -132,7 +132,7 @@ def __init__(self, parent, polynomial, check=True): if not isinstance(polynomial, Polynomial): raise TypeError("polynomial must be a polynomial") - if not polynomial in parent.polynomial_ring(): + if polynomial not in parent.polynomial_ring(): raise TypeError("polynomial must be in the polynomial ring of the parent") f = parent.modulus() diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 82ff837a2b5..02b56f18f41 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -1109,7 +1109,7 @@ def _mpoly_base_ring(self, variables=None): variables = self.variable_names_recursive() variables = list(variables) var = self.variable_name() - if not var in variables: + if var not in variables: return self else: try: diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 289c80160c3..c07c93adb38 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -944,7 +944,7 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): key = ("pbori", names, n, order) R = _get_from_cache(key) - if not R is None: + if R is not None: return R from sage.rings.polynomial.pbori.pbori import BooleanPolynomialRing @@ -953,6 +953,6 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): _save_in_cache(key, R) return R -######################################################################################### +############################################################################ # END (Factory function for making polynomial rings) -######################################################################################### +############################################################################ diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 9d0534186e1..ff5ca7aeb8d 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -383,12 +383,11 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, # if isinstance(name, (int,integer.Integer)) or isinstance(arg2,(int,integer.Integer)): # deprecation(trac_number, "This behavior of PowerSeriesRing is being deprecated in favor of constructing multivariate power series rings. (See Trac ticket #1956.)") - # the following is the original, univariate-only code if isinstance(name, (int, integer.Integer)): default_prec = name - if not names is None: + if names is not None: name = names name = normalize_names(1, name) diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 1fc7b20e2fc..807dce92a9b 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -272,7 +272,7 @@ def QuotientRing(R, I, names=None, **kwds): #if not isinstance(R, commutative_ring.CommutativeRing): # raise TypeError, "R must be a commutative ring." from sage.all import Integers, ZZ - if not R in Rings(): + if R not in Rings(): raise TypeError("R must be a ring.") try: is_commutative = R.is_commutative() diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index fbd0fd79d65..a9c54184574 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -845,7 +845,7 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True): for i in range(len(S)): if S[i] == self.places()[0]: S[i] = -1 - if not b in self: + if b not in self: raise TypeError("second argument must be a rational number") b = self(b) if b == 0: @@ -1488,7 +1488,7 @@ def quadratic_defect(self, a, p, check=True): """ from sage.rings.all import Infinity from sage.arith.misc import legendre_symbol - if not a in self: + if a not in self: raise TypeError(str(a) + " must be an element of " + str(self)) if p.parent() == ZZ.ideal_monoid(): p = p.gen() From 4ae6832b6785d51eb08c2547b79024c6f3ce3df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Oct 2021 19:23:59 +0200 Subject: [PATCH 118/122] fix E714 and E713 in combinat --- src/sage/combinat/affine_permutation.py | 4 ++-- src/sage/combinat/baxter_permutations.py | 4 ++-- .../combinat/cluster_algebra_quiver/mutation_type.py | 4 ++-- src/sage/combinat/composition_tableau.py | 6 +++--- src/sage/combinat/crystals/generalized_young_walls.py | 2 +- src/sage/combinat/designs/bibd.py | 2 +- src/sage/combinat/designs/block_design.py | 3 ++- .../designs/orthogonal_arrays_build_recursive.py | 2 +- .../combinat/designs/steiner_quadruple_systems.py | 11 ++++++----- src/sage/combinat/diagram_algebras.py | 2 +- src/sage/combinat/fully_packed_loop.py | 4 ++-- src/sage/combinat/k_tableau.py | 10 +++++----- src/sage/combinat/matrices/hadamard_matrix.py | 4 ++-- src/sage/combinat/output.py | 4 +++- src/sage/combinat/partition_tuple.py | 6 +++--- src/sage/combinat/root_system/associahedron.py | 3 ++- src/sage/combinat/root_system/dynkin_diagram.py | 2 +- .../combinat/root_system/root_lattice_realizations.py | 2 +- src/sage/combinat/sf/jack.py | 2 +- src/sage/combinat/sidon_sets.py | 4 ++-- src/sage/combinat/tableau_tuple.py | 4 ++-- src/sage/combinat/words/finite_word.py | 2 +- 22 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index 8caffd1a81e..0e252b85625 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -1707,7 +1707,7 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: sage: G = AffinePermutationGroup(['G',2,1]) - sage: p=G([2, 10, -5, 12, -3, 5]) + sage: p = G([2, 10, -5, 12, -3, 5]) sage: p.apply_simple_reflection_right(0) Type G affine permutation with window [-9, -1, -5, 12, 8, 16] sage: p.apply_simple_reflection_right(1) @@ -1715,7 +1715,7 @@ def apply_simple_reflection_right(self, i): sage: p.apply_simple_reflection_right(2) Type G affine permutation with window [2, -5, 10, -3, 12, 5] """ - if not i in self.parent().index_set(): + if i not in self.parent().index_set(): raise ValueError('index not in index set') j = i l = self[:] diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index 8464f8d27b3..59d9a0256fb 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -117,7 +117,7 @@ def __contains__(self, x): sage: sorted([p for p in Permutations(6) if p in BaxterPermutations(6)]) == sorted(BaxterPermutations(6).list()) True """ - if not x in Permutations(self._n): + if x not in Permutations(self._n): return False for i in range(1, len(x) - 1): a = x[i] @@ -299,7 +299,7 @@ def __contains__(self, x): sage: 42 in BaxterPermutations() False """ - if not x in Permutations(): + if x not in Permutations(): return False return x in BaxterPermutations(len(x)) diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 7a843e423f3..7a728780003 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -120,7 +120,7 @@ def _triangles(dg): for e in E: v1, v2 = e for v in V: - if not v in e: + if v not in e: if (v, v1) in E: if (v2, v) in E: flat_trian = sorted([v,v1,v2]) @@ -1165,7 +1165,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): connecting_vertices.append( v ) # if a vertex is of valence two and contained in an oriented 3-cycle, it is a connecting vertex elif w[0] == 1 and w[1] == 1: - if v in o_trian_verts and not v in long_cycle_verts: + if v in o_trian_verts and v not in long_cycle_verts: connecting_vertices.append( v ) # post-parsing 1: if we are in the affine type A case, the two parameters for the non-oriented long cycle are computed diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index 7c563bfffd1..0f8d70549f5 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -409,7 +409,7 @@ def __classcall_private__(cls, *args, **kwargs): if shape is not None: # use in (and not isinstance) below so that lists can be used as # shorthand - if not shape in Compositions(): + if shape not in Compositions(): raise ValueError("shape must be a composition") if any(i == 0 for i in shape): raise ValueError("shape must have non-zero parts") @@ -475,8 +475,8 @@ def _element_constructor_(self, t): ... ValueError: [[1], [1, 2]] is not an element of Composition Tableaux of size 3 and maximum entry 3. """ - if not t in self: - raise ValueError("%s is not an element of %s."%(t, self)) + if t not in self: + raise ValueError("%s is not an element of %s." % (t, self)) return self.element_class(self, t) diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index d656d27080f..60c47154c8e 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -711,7 +711,7 @@ def in_highest_weight_crystal(self,La): sage: x.in_highest_weight_crystal(La) False """ - if not La in self.parent().weight_lattice_realization(): + if La not in self.parent().weight_lattice_realization(): raise TypeError("Must be an element in the weight lattice realization") ac = self.parent().weight_lattice_realization().simple_coroots() n = self.cartan_type().classical().rank() diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 44f8fbe70bc..f6af1e56990 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -958,7 +958,7 @@ def PBD_4_5_8_9_12(v, check=True): sage: for v in (0,1,4,5,8,9,12,13,16,17,20,21,24,25): ....: _ = PBD_4_5_8_9_12(v) """ - if not v%4 in [0,1]: + if v % 4 not in [0, 1]: raise ValueError if v <= 1: PBD = [] diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 65f49d9a864..abdaca1e17b 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -676,7 +676,7 @@ def projective_plane_to_OA(pplane, pt=None, check=True): assert all(len(B) == n+1 for B in pplane), "pplane is not a projective plane" pplane = _relabel_bibd(pplane,n**2+n+1,p=n**2+n) - OA = [[x%n for x in sorted(X)] for X in pplane if not n**2+n in X] + OA = [[x % n for x in sorted(X)] for X in pplane if n**2+n not in X] assert len(OA) == n**2, "pplane is not a projective plane" @@ -686,6 +686,7 @@ def projective_plane_to_OA(pplane, pt=None, check=True): return OA + def projective_plane(n, check=True, existence=False): r""" Return a projective plane of order ``n`` as a 2-design. diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 103069b1b16..17cf84606b5 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -545,7 +545,7 @@ def construction_q_x(k,q,x,check=True,explain_construction=False): relabel = {i:j for j,i in enumerate(points_to_keep)} # PBD is a (n,[q,q-x-1,q-x+1,x+2])-PBD - PBD = [[relabel[xx] for xx in B if not xx in points_to_delete] for B in TD] + PBD = [[relabel[xx] for xx in B if xx not in points_to_delete] for B in TD] # Taking the unique block of size x+2 assert list(map(len,PBD)).count(x+2)==1 diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index 5f49cdd3daf..1cba16ea939 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -545,14 +545,15 @@ def _missing_pair(n,l): sage: _missing_pair(6, [(0,1), (4,5)]) (2, 3) """ - l = [x for X in l for x in X] + l = set(x for X in l for x in X) for x in range(n): - if not x in l: + if x not in l: break - assert not x in l - assert not x+1 in l - return (x,x+1) + assert x not in l + assert x + 1 not in l + return (x, x + 1) + def barP(eps, m): r""" diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 17a9f85bc5f..e7686fabe95 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -2771,7 +2771,7 @@ def s(self, i): sage: P2h.s(1) P{{-3, 3}, {-2, 1}, {-1, 2}} """ - if not i in ZZ or i <= 0 or i >= self._k: + if i not in ZZ or i <= 0 or i >= self._k: raise ValueError("i must be an integer between 1 and {}".format(self._k-1)) B = self.basis() SP = B.keys() diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index 05e4ab5fd25..81eb5131349 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -88,10 +88,10 @@ def _make_color_list(n, colors=None, color_map=None, randomize=False): elif color_map: from matplotlib import cm - if not color_map in cm.datad: + if color_map not in cm.datad: raise ValueError('unknown color map %s' % color_map) cmap = cm.__dict__[color_map] - colors = [cmap(i/float(n-1))[:3] for i in range(n)] + colors = [cmap(i / float(n - 1))[:3] for i in range(n)] if colors and randomize: from sage.misc.prandom import shuffle diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 1e9e57b8d86..17fccecd104 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -812,7 +812,7 @@ def check(self): if not self.parent()._weight == WeakTableau_bounded.from_core_tableau(self,self.k).weight(): raise ValueError("The weight of the parent does not agree with the weight of the tableau!") t = SkewTableau(list(self)) - if not t in SemistandardSkewTableaux(): + if t not in SemistandardSkewTableaux(): raise ValueError("The tableau is not semistandard!") outer = Core(t.outer_shape(),self.k+1) inner = Core(t.inner_shape(),self.k+1) @@ -1538,7 +1538,7 @@ def check(self): ValueError: The tableaux is not semistandard! """ t = SkewTableau(list(self)) - if not t in SemistandardSkewTableaux(): + if t not in SemistandardSkewTableaux(): raise ValueError("The tableaux is not semistandard!") if not self.parent()._weight == tuple(t.weight()): raise ValueError("The weight of the parent does not agree with the weight of the tableau!") @@ -4449,7 +4449,7 @@ def cells_head_dictionary( cls, T ): nextv = ST.entries_by_content(i + 1) for c in ST.cells_by_content(i): v = T[c[0]][c[1]] - if not v in nextv: + if v not in nextv: if v in dout: dout[v] += [c] else: @@ -4457,7 +4457,7 @@ def cells_head_dictionary( cls, T ): return dout @classmethod - def marked_CST_to_transposition_sequence( self, T, k ): + def marked_CST_to_transposition_sequence(self, T, k): """ Return a list of transpositions corresponding to ``T``. @@ -4533,7 +4533,7 @@ def marked_CST_to_transposition_sequence( self, T, k ): MM = [[LL[a][b] for b in range(len(LL[a])) if (a,b) in mcells] for a in range(len(mP))] transeq = self.marked_CST_to_transposition_sequence(MM, k) - if not transeq is None: + if transeq is not None: return [[j-l, j+1]] + transeq @classmethod diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 6380e580448..d12f3e644c4 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -633,7 +633,7 @@ def true(): M = RSHCD_324(e) elif ( e == 1 and n%16 == 0 and - not sqn is None and + sqn is not None and is_prime_power(sqn-1) and is_prime_power(sqn+1)): if existence: @@ -641,7 +641,7 @@ def true(): M = -rshcd_from_close_prime_powers(sqn) elif ( e == 1 and - not sqn is None and + sqn is not None and sqn%4 == 2 and strongly_regular_graph(sqn-1,(sqn-2)//2,(sqn-6)//4, existence=True) is True and diff --git a/src/sage/combinat/output.py b/src/sage/combinat/output.py index 563795a49f4..95b5cd0c046 100644 --- a/src/sage/combinat/output.py +++ b/src/sage/combinat/output.py @@ -288,7 +288,9 @@ def tex_from_skew_array(array, with_lines=False, align='b'): # function end_line which puts in the required \cline's. if with_lines: # last position of None in each row - nones=[1 if not None in row else 1+len(row)-row[::-1].index(None) for row in array] + nones = [1 if None not in row else 1 + len(row) - row[::-1].index(None) + for row in array] + def end_line(r): # in a slightly unpythonic way, we label the lines as 0, 1, ..., len(array) if r==0: diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 34b5a9e46ff..f992d013a0d 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -2706,16 +2706,16 @@ def __init__(self, level, regular): sage: RPT = PartitionTuples(level=4, regular=3) sage: TestSuite(RPT).run() """ - if not level in NN: + if level not in NN: raise ValueError('level must be a non-negative integer') if not isinstance(regular, tuple): # This should not happen if called from RegularPartitionTuples regular = (regular,) * level - if any (r != 1 for r in regular): + if any(r != 1 for r in regular): category = InfiniteEnumeratedSets() else: category = FiniteEnumeratedSets() - if any (r not in NN for r in regular): + if any(r not in NN for r in regular): raise ValueError('regular must be a tuple of non-negative integers') if len(regular) != level: raise ValueError("regular must be a tuple with length {}".format(level)) diff --git a/src/sage/combinat/root_system/associahedron.py b/src/sage/combinat/root_system/associahedron.py index 30092073473..eef083511f1 100644 --- a/src/sage/combinat/root_system/associahedron.py +++ b/src/sage/combinat/root_system/associahedron.py @@ -299,7 +299,7 @@ def Associahedra(base_ring, ambient_dim, backend='ppl'): :class:`Associahedra_base`. """ - if not base_ring is QQ: + if base_ring is not QQ: raise NotImplementedError("base ring must be QQ") if backend == 'ppl': return Associahedra_ppl(base_ring, ambient_dim, backend) @@ -314,6 +314,7 @@ def Associahedra(base_ring, ambient_dim, backend='ppl'): else: raise ValueError("unknown backend") + class Associahedra_base(object): """ Base class of parent of Associahedra of specified dimension diff --git a/src/sage/combinat/root_system/dynkin_diagram.py b/src/sage/combinat/root_system/dynkin_diagram.py index 809afeecb6f..3c889d5e0ea 100644 --- a/src/sage/combinat/root_system/dynkin_diagram.py +++ b/src/sage/combinat/root_system/dynkin_diagram.py @@ -522,7 +522,7 @@ def dual(self): result.add_vertices(self.vertices()) for source, target, label in self.edges(): result.add_edge(target, source, label) - result._cartan_type = self._cartan_type.dual() if not self._cartan_type is None else None + result._cartan_type = self._cartan_type.dual() if self._cartan_type is not None else None return result def relabel(self, *args, **kwds): diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 95301036614..377ce53376c 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -723,7 +723,7 @@ def nonparabolic_positive_roots(self, index_set = None): if index_set is None: return [] return [x for x in self.positive_roots() - if not x in self.positive_roots(index_set)] + if x not in self.positive_roots(index_set)] @cached_method def nonparabolic_positive_root_sum(self, index_set=None): diff --git a/src/sage/combinat/sf/jack.py b/src/sage/combinat/sf/jack.py index 74d02ab7c53..5ca78639572 100644 --- a/src/sage/combinat/sf/jack.py +++ b/src/sage/combinat/sf/jack.py @@ -1215,7 +1215,7 @@ def _h_cache(self, n): to_cache_2 = self._h_to_self_cache[n] for mu in from_cache_1: for la in from_cache_1[mu]: - if not la in to_cache_1: + if la not in to_cache_1: to_cache_1[la] = {} to_cache_2[la] = {} to_cache_2[la][mu] = from_cache_1[mu][la] diff --git a/src/sage/combinat/sidon_sets.py b/src/sage/combinat/sidon_sets.py index dda3cd07d76..e9924762024 100644 --- a/src/sage/combinat/sidon_sets.py +++ b/src/sage/combinat/sidon_sets.py @@ -126,10 +126,10 @@ def sidon_sets_rec(N, g=1): sidons = set(pre_sidons) for psid in pre_sidons: psid_shift = Set([n - 1 for n in psid if n != 1] + [N - 1]) - if not psid_shift in pre_sidons: + if psid_shift not in pre_sidons: continue - if not 1 in psid: + if 1 not in psid: add_sid = True else: add_sid = True diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index ac3f23b1375..a7b98a63743 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -2243,8 +2243,8 @@ def _element_constructor_(self, t): ... ValueError: [[1, 2]] is not an element of Tableau tuples of level 3 """ - if not t in self: - raise ValueError("%s is not an element of %s"%(t, self)) + if t not in self: + raise ValueError("%s is not an element of %s" % (t, self)) # one way or another these two cases need to be treated separately if t == [] or t == [[]]: diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index e643ca9bbb3..609e701f7c7 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -3864,7 +3864,7 @@ def subword_complementaries(self, other): from sage.combinat.words.word import Word comp_words = [] for sp in selfpos: # list with positions of one occurrence of `self` - comp_pos = [i for i in range(lo) if not i in set(sp)] + comp_pos = (i for i in range(lo) if i not in set(sp)) comp_words.append(Word([other[i] for i in comp_pos])) return comp_words From 623537cc94efbab3ed1d6d0152b1b353dec26c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Oct 2021 20:18:46 +0200 Subject: [PATCH 119/122] last fixes for relint : remove sagenb check and __metaclass_ --- src/.relint.yml | 4 +- src/sage/misc/bindable_class.py | 19 ++----- src/sage/misc/classcall_metaclass.pyx | 2 - src/sage/misc/nested_class.pyx | 72 +++++++++------------------ src/sage/misc/sageinspect.py | 12 ++--- src/sage_docbuild/ext/sage_autodoc.py | 3 +- 6 files changed, 36 insertions(+), 76 deletions(-) diff --git a/src/.relint.yml b/src/.relint.yml index 9d12054b982..19e935d93c0 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -7,8 +7,8 @@ hint: | # ifilter, imap, izip # __metaclass__ Hint: # update raise statements # except Exception, var - Hint: # sagenb # six is no longer allowed - pattern: '(import.*[, ]ifilter|import.*[, ]imap|import.*[, ]izip|^\s*raise\s*[A-Za-z]*Error\s*,|__metaclass__|except\s*[A-Za-z]\s*,|[^_]sagenb|import six|from six import)' + Hint: # six is no longer allowed + pattern: '(import.*[, ]ifilter|import.*[, ]imap|import.*[, ]izip|^\s*raise\s*[A-Za-z]*Error\s*,|__metaclass__|except\s*[A-Za-z]\s*,|import six|from six import)' filePattern: .*[.](py|pyx|rst) - name: 'foreign_latex: foreign commands in LaTeX' diff --git a/src/sage/misc/bindable_class.py b/src/sage/misc/bindable_class.py index c776faf7257..f8e557606b2 100644 --- a/src/sage/misc/bindable_class.py +++ b/src/sage/misc/bindable_class.py @@ -1,18 +1,15 @@ """ Bindable classes """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Nicolas M. Thiery # # 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/ -#***************************************************************************** - - +# https://www.gnu.org/licenses/ +# **************************************************************************** import functools from sage.misc.nested_class import NestedClassMetaclass from sage.misc.classcall_metaclass import ClasscallMetaclass @@ -33,16 +30,12 @@ class BindableClass(metaclass=ClasscallMetaclass): Let us consider the following class ``Outer`` with a nested class ``Inner``:: sage: from sage.misc.nested_class import NestedClassMetaclass - sage: class Outer: - ....: __metaclass__ = NestedClassMetaclass # just a workaround for Python misnaming nested classes - ....: + sage: class Outer(metaclass=NestedClassMetaclass): ....: class Inner: ....: def __init__(self, *args): ....: print(args) - ....: ....: def f(self, *args): ....: print("{} {}".format(self, args)) - ....: ....: @staticmethod ....: def f_static(*args): ....: print(args) @@ -84,9 +77,7 @@ class BindableClass(metaclass=ClasscallMetaclass): :class:`BindableClass` gives this binding behavior to all its subclasses:: sage: from sage.misc.bindable_class import BindableClass - sage: class Outer: - ....: __metaclass__ = NestedClassMetaclass # just a workaround for Python misnaming nested classes - ....: + sage: class Outer(metaclass=NestedClassMetaclass): ....: class Inner(BindableClass): ....: " some documentation " ....: def __init__(self, outer, *args): diff --git a/src/sage/misc/classcall_metaclass.pyx b/src/sage/misc/classcall_metaclass.pyx index 464a3ea93ec..ab19a5eb7a1 100644 --- a/src/sage/misc/classcall_metaclass.pyx +++ b/src/sage/misc/classcall_metaclass.pyx @@ -217,12 +217,10 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): We now show the usage of ``__classcall_private__``:: sage: class FooNoInherits(object, metaclass=ClasscallMetaclass): - ....: __metaclass__ = ClasscallMetaclass ....: @staticmethod ....: def __classcall_private__(cls): ....: print("calling private classcall") ....: return type.__call__(cls) - ... sage: FooNoInherits() calling private classcall <__main__.FooNoInherits object at ...> diff --git a/src/sage/misc/nested_class.pyx b/src/sage/misc/nested_class.pyx index 3054461a9c7..08750233543 100644 --- a/src/sage/misc/nested_class.pyx +++ b/src/sage/misc/nested_class.pyx @@ -44,24 +44,16 @@ EXAMPLES:: sage: A1.A2.A3.__name__ 'A3' - sage: A1.A2.A3 # py2 - - sage: A1.A2.A3 # py3 + sage: A1.A2.A3 - sage: nested_pickle(A1) # py2 - - sage: nested_pickle(A1) # py3 + sage: nested_pickle(A1) - sage: A1.A2 # py2 - - sage: A1.A2 # py3 + sage: A1.A2 - sage: A1.A2.A3 # py2 - - sage: A1.A2.A3 # py3 + sage: A1.A2.A3 sage: A1.A2.A3.__name__ 'A1.A2.A3' @@ -79,22 +71,13 @@ All of this is not perfect. In the following scenario:: sage: class B1: ....: A2 = A1.A2 - sage: nested_pickle(A1) # py2 - - sage: nested_pickle(B1) # py2 - - sage: A1.A2 # py2 - - sage: B1.A2 # py2 - - - sage: nested_pickle(A1) # py3 + sage: nested_pickle(A1) - sage: nested_pickle(B1) # py3 + sage: nested_pickle(B1) - sage: A1.A2 # py3 + sage: A1.A2 - sage: B1.A2 # py3 + sage: B1.A2 The name for ``"A1.A2"`` could potentially be set to ``"B1.A2"``. But that will work anyway. @@ -161,14 +144,9 @@ cpdef modify_for_nested_pickle(cls, str name_prefix, module, first_run=True): Note that the class is now found in the module under both its old and its new name:: - sage: getattr(module, 'A.B', 'Not found') # py2 - - sage: getattr(module, 'X.A.B', 'Not found') # py2 - - - sage: getattr(module, 'A.B', 'Not found') # py3 + sage: getattr(module, 'A.B', 'Not found') - sage: getattr(module, 'X.A.B', 'Not found') # py3 + sage: getattr(module, 'X.A.B', 'Not found') TESTS: @@ -253,9 +231,7 @@ def nested_pickle(cls): sage: A.B.__name__ 'A.B' - sage: getattr(module, 'A.B', 'Not found') # py2 - - sage: getattr(module, 'A.B', 'Not found') # py3 + sage: getattr(module, 'A.B', 'Not found') In Python 2.6, decorators work with classes; then ``@nested_pickle`` @@ -288,10 +264,8 @@ cdef class NestedClassMetaclass(type): derived subclass:: sage: from sage.misc.nested_class import NestedClassMetaclass - sage: class ASuperClass(object): # py2 - ....: __metaclass__ = NestedClassMetaclass # py2 - sage: class ASuperClass(object, metaclass=NestedClassMetaclass): # py3 - ....: pass # py3 + sage: class ASuperClass(object, metaclass=NestedClassMetaclass): + ....: pass sage: class A3(ASuperClass): ....: class B(object): ....: pass @@ -304,16 +278,16 @@ cdef class NestedClassMetaclass(type): r""" This invokes the nested_pickle on construction. - sage: from sage.misc.nested_class import NestedClassMetaclass - sage: class A(object): - ....: __metaclass__ = NestedClassMetaclass - ....: class B(object): - ....: pass - ... - sage: A.B - - sage: getattr(sys.modules['__main__'], 'A.B', 'Not found') - + EXAMPLES:: + + sage: from sage.misc.nested_class import NestedClassMetaclass + sage: class A(object, metaclass=NestedClassMetaclass): + ....: class B(object): + ....: pass + sage: A.B + + sage: getattr(sys.modules['__main__'], 'A.B', 'Not found') + """ modify_for_nested_pickle(self, self.__name__, sys_modules[self.__module__]) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 2ff2762f156..48c95b0f226 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -192,9 +192,9 @@ def loadable_module_extension(): def isclassinstance(obj): r""" - Checks if argument is instance of non built-in class + Check if argument is instance of non built-in class - INPUT: ``obj`` - object + INPUT: ``obj`` -- object EXAMPLES:: @@ -207,12 +207,10 @@ def isclassinstance(obj): sage: isclassinstance(myclass) False sage: class mymetaclass(type): pass - sage: class myclass2: - ....: __metaclass__ = mymetaclass + sage: class myclass2(metaclass=mymetaclass): pass sage: isclassinstance(myclass2) False """ - builtin_mods = set(['__builtin__', 'builtins', 'exceptions']) return (not inspect.isclass(obj) and @@ -1630,7 +1628,7 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return except (TypeError, AttributeError): pass if isclassinstance(obj): - if hasattr(obj,'_sage_src_'): #it may be a decorator! + if hasattr(obj, '_sage_src_'): #it may be a decorator! source = sage_getsource(obj) try: # we try to find the definition and parse it by @@ -2384,7 +2382,7 @@ class Element(object): # Check if we deal with an instance if isclassinstance(obj): - if isinstance(obj,functools.partial): + if isinstance(obj, functools.partial): return sage_getsourcelines(obj.func) else: return sage_getsourcelines(obj.__class__) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index b698a6e9297..399ac1532e5 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -1174,8 +1174,7 @@ def import_object(self): # by using the metaclass NestedMetaclass, we change the attribute # __name__ of the nested class. For example, in # - # class A: - # __metaclass__ = NestedClassMetaclass + # class A(metaclass=NestedMetaclass): # class B(object): # pass # From c2cba3250e53ffb193c5696a6c7c7636a32d2d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Oct 2021 20:29:49 +0200 Subject: [PATCH 120/122] one more fix --- .../dynamics/arithmetic_dynamics/endPN_automorphism_group.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index e5ef59acf59..75a863423eb 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -1790,8 +1790,9 @@ def which_group(list_of_elements): else: return ['A_5'] + def conjugating_set_initializer(f, g): - """ + r""" Return a conjugation invariant set together with information to reduce the combinatorics of checking all possible conjugations. From c8499d1a703a6f50e157a9270ff35b9a0a4772b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Oct 2021 21:46:31 +0200 Subject: [PATCH 121/122] fix E713 and E714 in schemes --- src/sage/schemes/affine/affine_homset.py | 2 +- src/sage/schemes/affine/affine_subscheme.py | 2 +- src/sage/schemes/curves/affine_curve.py | 8 ++++---- src/sage/schemes/curves/curve.py | 4 ++-- src/sage/schemes/curves/projective_curve.py | 4 ++-- src/sage/schemes/elliptic_curves/cm.py | 3 +-- src/sage/schemes/elliptic_curves/ell_egros.py | 6 +++--- .../elliptic_curves/ell_modular_symbols.py | 10 +++++----- .../elliptic_curves/ell_rational_field.py | 11 ++++++----- .../schemes/elliptic_curves/isogeny_class.py | 9 +++++---- .../elliptic_curves/isogeny_small_degree.py | 16 ++++++++-------- src/sage/schemes/elliptic_curves/kraus.py | 2 +- src/sage/schemes/generic/scheme.py | 6 +++--- src/sage/schemes/plane_conics/con_field.py | 2 +- .../plane_quartics/quartic_constructor.py | 4 ++-- src/sage/schemes/projective/projective_homset.py | 2 +- .../schemes/projective/projective_morphism.py | 10 ++++++---- .../schemes/projective/projective_subscheme.py | 2 +- src/sage/schemes/toric/homset.py | 2 +- src/sage/schemes/toric/toric_subscheme.py | 8 ++++---- 20 files changed, 58 insertions(+), 55 deletions(-) diff --git a/src/sage/schemes/affine/affine_homset.py b/src/sage/schemes/affine/affine_homset.py index 59c842901c4..f712c0f130d 100644 --- a/src/sage/schemes/affine/affine_homset.py +++ b/src/sage/schemes/affine/affine_homset.py @@ -398,7 +398,7 @@ def numerical_points(self, F=None, **kwds): from sage.schemes.affine.affine_space import is_AffineSpace if F is None: F = CC - if not F in Fields() or not hasattr(F, 'precision'): + if F not in Fields() or not hasattr(F, 'precision'): raise TypeError('F must be a numerical field') X = self.codomain() if X.base_ring() not in NumberFields(): diff --git a/src/sage/schemes/affine/affine_subscheme.py b/src/sage/schemes/affine/affine_subscheme.py index d2938dc1dcb..92b040b9631 100644 --- a/src/sage/schemes/affine/affine_subscheme.py +++ b/src/sage/schemes/affine/affine_subscheme.py @@ -328,7 +328,7 @@ def is_smooth(self, point=None): False """ R = self.ambient_space().coordinate_ring() - if not point is None: + if point is not None: self._check_satisfies_equations(point) point_subs = dict(zip(R.gens(), point)) Jac = self.Jacobian().subs(point_subs) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 880b420b7b6..2702be6f095 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -678,7 +678,7 @@ def tangents(self, P, factor=True): # divide T by that power of vars[1] T = self.ambient_space().coordinate_ring()(dict([((v[0],v[1] - t), h) for (v,h) in T.dict().items()])) # T is homogeneous in var[0], var[1] if nonconstant, so dehomogenize - if not T in self.base_ring(): + if T not in self.base_ring(): if T.degree(vars[0]) > 0: T = T(vars[0], 1) roots = T.univariate_polynomial().roots() @@ -982,7 +982,7 @@ def projection(self, indices, AS=None): raise ValueError("(=%s) must be a list or tuple of length between 2 and (=%s), inclusive" % (indices, n - 1)) if len(set(indices)) < len(indices): raise ValueError("(=%s) must be a list or tuple of distinct indices or variables" % indices) - if not AS is None: + if AS is not None: if not is_AffineSpace(AS): raise TypeError("(=%s) must be an affine space" % AS) if AS.dimension_relative() != len(indices): @@ -2145,11 +2145,11 @@ def _nonsingular_model(self): basis = list(gbasis) syzygy = {} for i in range(n): - S = k[R._first_ngens(i+1)] + S = k[R._first_ngens(i + 1)] while basis: f = basis.pop() if f in S: - if not i in syzygy and f: + if i not in syzygy and f: syzygy[i] = f else: basis.append(f) diff --git a/src/sage/schemes/curves/curve.py b/src/sage/schemes/curves/curve.py index 2e4c41d961d..f9cea384986 100644 --- a/src/sage/schemes/curves/curve.py +++ b/src/sage/schemes/curves/curve.py @@ -320,8 +320,8 @@ def singular_points(self, F=None): if not self.base_ring() in Fields(): raise TypeError("curve must be defined over a field") F = self.base_ring() - elif not F in Fields(): - raise TypeError("(=%s) must be a field"%F) + elif F not in Fields(): + raise TypeError("(=%s) must be a field" % F) X = self.singular_subscheme() return [self.point(p, check=False) for p in X.rational_points(F=F)] diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 8d8db455481..ea49a181815 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -411,7 +411,7 @@ def projection(self, P=None, PS=None): raise TypeError("this curve is already a plane curve") if self.base_ring() not in Fields(): raise TypeError("this curve must be defined over a field") - if not PS is None: + if PS is not None: if not is_ProjectiveSpace(PS): raise TypeError("(=%s) must be a projective space" % PS) if PS.dimension_relative() != n - 1: @@ -455,7 +455,7 @@ def projection(self, P=None, PS=None): Q = self(P) except TypeError: pass - if not Q is None: + if Q is not None: raise TypeError("(=%s) must be a point not on this curve" % P) try: Q = self.ambient_space()(P) diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index 7d3d20d15ff..9556d1c8ac5 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -623,9 +623,8 @@ def is_cm_j_invariant(j, method='new'): True """ # First we check that j is an algebraic number: - from sage.rings.all import NumberFieldElement, NumberField - if not isinstance(j, NumberFieldElement) and not j in QQ: + if not isinstance(j, NumberFieldElement) and j not in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") # for j in ZZ we have a lookup-table: diff --git a/src/sage/schemes/elliptic_curves/ell_egros.py b/src/sage/schemes/elliptic_curves/ell_egros.py index 09ed54aa31f..bc782475f2e 100644 --- a/src/sage/schemes/elliptic_curves/ell_egros.py +++ b/src/sage/schemes/elliptic_curves/ell_egros.py @@ -447,10 +447,10 @@ def egros_get_j(S=[], proof=None, verbose=False): P = urst(P) x = P[0] y = P[1] - j = x**3 /w - assert j-1728 == y**2 /w + j = x**3 / w + assert j - 1728 == y**2 / w if is_possible_j(j, S): - if not j in jlist: + if j not in jlist: if verbose: print("Adding possible j = ", j) sys.stdout.flush() diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 30a61e1635c..7a0beab252f 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -129,7 +129,7 @@ def modular_symbol_space(E, sign, base_ring, bound=None): Modular Symbols space of dimension 1 for Gamma_0(11) of weight 2 with sign -1 over Finite Field of size 37 """ - if not sign in [-1,0,1]: + if sign not in [-1, 0, 1]: raise TypeError('sign must -1, 0 or 1') N = E.conductor() M = ModularSymbols(N, sign=sign, base_ring=base_ring) @@ -323,12 +323,12 @@ def __init__(self, E, sign, nap=1000): """ from sage.libs.eclib.newforms import ECModularSymbol - if not sign in [-1,1]: + if sign not in [-1, 1]: raise TypeError('sign must -1 or 1') self._sign = ZZ(sign) self._E = E self._scaling = 1 if E.discriminant()>0 else ZZ(1)/2 - self._implementation="eclib" + self._implementation = "eclib" self._base_ring = QQ # The ECModularSymbol class must be initialized with sign=0 to compute minus symbols self._modsym = ECModularSymbol(E, int(sign==1), nap) @@ -436,11 +436,11 @@ def __init__(self, E, sign, normalize="L_ratio"): [1, 1, 1, 1, 1, 1, 1, 1] """ - if not sign in [-1,1]: + if sign not in [-1, 1]: raise TypeError('sign must -1 or 1') self._sign = ZZ(sign) self._E = E - self._implementation="sage" + self._implementation = "sage" self._normalize = normalize self._modsym = E.modular_symbol_space(sign=self._sign) self._base_ring = self._modsym.base_ring() diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 847a1485f8b..b0b674a0bde 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2376,12 +2376,12 @@ def _compute_gens(self, proof, # progress (see trac #1949). X = self.mwrank('-p 100 -S '+str(sat_bound)) verbose_verbose("Calling mwrank shell.") - if not 'The rank and full Mordell-Weil basis have been determined unconditionally' in X: + if 'The rank and full Mordell-Weil basis have been determined unconditionally' not in X: msg = 'Generators not provably computed.' if proof: - raise RuntimeError('%s\n%s'%(X,msg)) + raise RuntimeError('%s\n%s' % (X, msg)) else: - verbose_verbose("Warning -- %s"%msg, level=1) + verbose_verbose("Warning -- %s" % msg, level=1) proved = False else: proved = True @@ -4714,8 +4714,9 @@ def isogenies_prime_degree(self, l=None): if isinstance(l, list): isogs = [] i = 0 - while i """ from sage.categories.commutative_rings import CommutativeRings - if not R in CommutativeRings(): + if R not in CommutativeRings(): raise TypeError("R (={}) must be a commutative ring".format(R)) self.__R = R - if not S is None: - if not S in CommutativeRings(): + if S is not None: + if S not in CommutativeRings(): raise TypeError("S (={}) must be a commutative ring".format(S)) if not R.has_coerce_map_from(S): raise ValueError("There must be a natural map S --> R, but S = {} and R = {}".format(S, R)) diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 0d5eb412034..1b557fcd735 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -878,7 +878,7 @@ def parametrization(self, point=None, morphism=True): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ - if (not self._parametrization is None) and not point: + if (self._parametrization is not None) and not point: par = self._parametrization else: if not self.is_smooth(): diff --git a/src/sage/schemes/plane_quartics/quartic_constructor.py b/src/sage/schemes/plane_quartics/quartic_constructor.py index e2a93bc7c2e..4b459b3c718 100644 --- a/src/sage/schemes/plane_quartics/quartic_constructor.py +++ b/src/sage/schemes/plane_quartics/quartic_constructor.py @@ -58,9 +58,9 @@ def QuarticCurve(F, PP=None, check=False): if not(F.is_homogeneous() and F.degree()==4): raise ValueError("Argument F (=%s) must be a homogeneous polynomial of degree 4"%F) - if not PP is None: + if PP is not None: if not is_ProjectiveSpace(PP) and PP.dimension == 2: - raise ValueError("Argument PP (=%s) must be a projective plane"%PP) + raise ValueError(f"Argument PP (={PP}) must be a projective plane") else: PP = ProjectiveSpace(P) diff --git a/src/sage/schemes/projective/projective_homset.py b/src/sage/schemes/projective/projective_homset.py index 8ed8c8af66a..e3d40a6179f 100644 --- a/src/sage/schemes/projective/projective_homset.py +++ b/src/sage/schemes/projective/projective_homset.py @@ -373,7 +373,7 @@ def numerical_points(self, F=None, **kwds): from sage.schemes.projective.projective_space import is_ProjectiveSpace if F is None: F = CC - if not F in Fields() or not hasattr(F, 'precision'): + if F not in Fields() or not hasattr(F, 'precision'): raise TypeError('F must be a numerical field') X = self.codomain() if X.base_ring() not in NumberFields(): diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 1aa7ea7f841..1224b82feed 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1594,18 +1594,20 @@ def rational_preimages(self, Q, k=1): k = ZZ(k) if k <= 0: raise ValueError("k (=%s) must be a positive integer" % k) - #first check if subscheme + # first check if subscheme from sage.schemes.projective.projective_subscheme import AlgebraicScheme_subscheme_projective if isinstance(Q, AlgebraicScheme_subscheme_projective): return Q.preimage(self, k) - #else assume a point + # else assume a point BR = self.base_ring() if k > 1 and not self.is_endomorphism(): raise TypeError("must be an endomorphism of projective space") - if not Q in self.codomain(): + if Q not in self.codomain(): raise TypeError("point must be in codomain of self") - if isinstance(BR.base_ring(),(ComplexField_class, RealField_class,RealIntervalField_class, ComplexIntervalField_class)): + if isinstance(BR.base_ring(), (ComplexField_class, RealField_class, + RealIntervalField_class, + ComplexIntervalField_class)): raise NotImplementedError("not implemented over precision fields") PS = self.domain().ambient_space() N = PS.dimension_relative() diff --git a/src/sage/schemes/projective/projective_subscheme.py b/src/sage/schemes/projective/projective_subscheme.py index abeeda9799b..ccda38ad316 100644 --- a/src/sage/schemes/projective/projective_subscheme.py +++ b/src/sage/schemes/projective/projective_subscheme.py @@ -434,7 +434,7 @@ def is_smooth(self, point=None): sage: H.is_smooth() # one of the few cases where the cone over the subvariety is smooth True """ - if not point is None: + if point is not None: self._check_satisfies_equations(point) R = self.ambient_space().coordinate_ring() point_subs = dict(zip(R.gens(), point)) diff --git a/src/sage/schemes/toric/homset.py b/src/sage/schemes/toric/homset.py index 88516ae0b5e..31bbddde13e 100644 --- a/src/sage/schemes/toric/homset.py +++ b/src/sage/schemes/toric/homset.py @@ -400,7 +400,7 @@ def _finite_field_enumerator(self, finite_field=None): variety = self.codomain() if finite_field is None: finite_field = variety.base_ring() - if not finite_field in FiniteFields(): + if finite_field not in FiniteFields(): raise ValueError('not a finite field') return FiniteFieldPointEnumerator(variety.fan(), finite_field) diff --git a/src/sage/schemes/toric/toric_subscheme.py b/src/sage/schemes/toric/toric_subscheme.py index 728063064cb..524d6fcd657 100644 --- a/src/sage/schemes/toric/toric_subscheme.py +++ b/src/sage/schemes/toric/toric_subscheme.py @@ -324,8 +324,8 @@ def affine_algebraic_patch(self, cone=None, names=None): # inhomogenize the Cox homogeneous polynomial with respect to the given cone inhomogenize = dict( (ambient.coordinate_ring().gen(i), 1) for i in range(0,fan.nrays()) - if not i in cone.ambient_ray_indices() ) - polynomials = [ p.subs(inhomogenize) for p in self.defining_polynomials() ] + if i not in cone.ambient_ray_indices() ) + polynomials = [p.subs(inhomogenize) for p in self.defining_polynomials()] # map the monomial x^{D_m} to m, see reference. n_rho_matrix = cone.rays().matrix() @@ -581,7 +581,7 @@ def is_smooth(self, point=None): sage: Y.is_smooth() True """ - if not point is None: + if point is not None: toric_patch = self.neighborhood(point) return toric_patch.is_smooth(toric_patch.embedding_center()) @@ -886,7 +886,7 @@ def is_smooth(self, point=None): sage: Y.is_smooth([0,0]) True """ - if not point is None: + if point is not None: self._check_satisfies_equations(point) if self.ambient_space().is_smooth(): R = self.ambient_space().coordinate_ring() From f716a0b366e31bbb546230140489244cfb68390d Mon Sep 17 00:00:00 2001 From: Release Manager Date: Fri, 29 Oct 2021 00:56:37 +0200 Subject: [PATCH 122/122] Updated SageMath version to 9.5.beta5 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index f80be3c909b..4a9c0e96236 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.5.beta4", - "version": "9.5.beta4", + "title": "sagemath/sage: 9.5.beta5", + "version": "9.5.beta5", "upload_type": "software", - "publication_date": "2021-10-19", + "publication_date": "2021-10-28", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.5.beta4", + "identifier": "https://github.com/sagemath/sage/tree/9.5.beta5", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index d035bc57e4b..c79c2d06798 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.5.beta4, Release Date: 2021-10-19 +SageMath version 9.5.beta5, Release Date: 2021-10-28 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 0b799f8f36f..858bfffb8f3 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=b789b0cad7547f9b699a305d5cfe6a4b67d353df -md5=ad65b7da919dcff932eb34a98793504b -cksum=2565222951 +sha1=ab6592f2216c48049bbbcfb3a42c3e157f44d917 +md5=fc3a50e0f47802e55969eade44648af4 +cksum=3704729867 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 7038ebe859c..0d3e25199f6 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -dc6e96cb4344704cb6779ff68e024d06063d50ee +c5a87834e7e2939b878a5134cd240e6c712bd573 diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index 3fe69eb5038..2b08eebb761 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.5.beta4 +9.5.beta5 diff --git a/src/VERSION.txt b/src/VERSION.txt index 3fe69eb5038..2b08eebb761 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.5.beta4 +9.5.beta5 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 1fa6283f152..ed215d6aeb0 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.5.beta4' -SAGE_RELEASE_DATE='2021-10-19' -SAGE_VERSION_BANNER='SageMath version 9.5.beta4, Release Date: 2021-10-19' +SAGE_VERSION='9.5.beta5' +SAGE_RELEASE_DATE='2021-10-28' +SAGE_VERSION_BANNER='SageMath version 9.5.beta5, Release Date: 2021-10-28' diff --git a/src/sage/version.py b/src/sage/version.py index 0cd77c29e31..a0bf87ed037 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.5.beta4' -date = '2021-10-19' -banner = 'SageMath version 9.5.beta4, Release Date: 2021-10-19' +version = '9.5.beta5' +date = '2021-10-28' +banner = 'SageMath version 9.5.beta5, Release Date: 2021-10-28'